aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2025-03-09 13:42:54 +0000
committerMark Brown <broonie@kernel.org>2025-03-09 13:42:54 +0000
commit8a7e7a03e3c53cd9abbbf233899cc2e05b2c6ec0 (patch)
tree5fcf293ed52414cb2b60aa728da2d7fe4298fb29
parentASoC: dmic: add regulator support (diff)
parentASoC: SOF: Intel: ptl: Add support for mic privacy (diff)
downloadwireguard-linux-8a7e7a03e3c53cd9abbbf233899cc2e05b2c6ec0.tar.xz
wireguard-linux-8a7e7a03e3c53cd9abbbf233899cc2e05b2c6ec0.zip
ASoC: SOF: Intel: Add support for ACE3+ mic privacy
Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>: ACE3 (Panther Lake) introduced support for microphone privacy feature which can - in hardware - mute incoming audio data based on a state of a physical switch. The change in the privacy state is delivered through interface IP blocks and can only be handled by the link owner. In Intel platforms Soundwire is for example host owned, so the interrupt can only be handled by the host. Since the input stream is going to be muted by hardware, the host needs to send a message to firmware about the change in privacy so it can execute a fade out/in to enhance user experience. The support for microphone privacy can be queried from the HW_CONFIG data under the INTEL_MIC_PRIVACY_CAP tuple. This is Intel specific data, the core will pass it to platform code if the intel_configure_mic_privacy() callback is provided. Platform code can call sof_ipc4_mic_privacy_state_change() to send the IPC message to the firmware on state change.
-rw-r--r--include/sound/hda-mlink.h25
-rw-r--r--include/sound/sof/ipc4/header.h13
-rw-r--r--sound/soc/sof/intel/Makefile2
-rw-r--r--sound/soc/sof/intel/hda-mlink.c127
-rw-r--r--sound/soc/sof/intel/hda.c34
-rw-r--r--sound/soc/sof/intel/hda.h4
-rw-r--r--sound/soc/sof/intel/lnl.c117
-rw-r--r--sound/soc/sof/intel/lnl.h6
-rw-r--r--sound/soc/sof/intel/mtl.c74
-rw-r--r--sound/soc/sof/intel/mtl.h15
-rw-r--r--sound/soc/sof/intel/pci-lnl.c12
-rw-r--r--sound/soc/sof/intel/pci-mtl.c8
-rw-r--r--sound/soc/sof/intel/pci-ptl.c17
-rw-r--r--sound/soc/sof/intel/ptl.c106
-rw-r--r--sound/soc/sof/intel/ptl.h19
-rw-r--r--sound/soc/sof/intel/shim.h2
-rw-r--r--sound/soc/sof/ipc4-loader.c33
-rw-r--r--sound/soc/sof/ipc4-priv.h5
-rw-r--r--sound/soc/sof/ipc4.c18
19 files changed, 470 insertions, 167 deletions
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h
index 6774f4b9e5fc..4327317be6af 100644
--- a/include/sound/hda-mlink.h
+++ b/include/sound/hda-mlink.h
@@ -62,6 +62,12 @@ struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid);
int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable);
+/* microphone privacy specific function supported by ACE3+ architecture */
+void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
+ unsigned long mask);
+bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid);
+bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid);
+
#else
static inline int
@@ -185,4 +191,23 @@ hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enabl
{
return 0;
}
+
+static inline void
+hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
+ unsigned long mask)
+{
+}
+
+static inline bool
+hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
+{
+ return false;
+}
+
+static inline bool
+hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
+{
+ return false;
+}
+
#endif /* CONFIG_SND_SOC_SOF_HDA_MLINK */
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index 0c0cf47946b1..f71d04736d17 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -396,6 +396,7 @@ enum sof_ipc4_base_fw_params {
SOF_IPC4_FW_PARAM_MODULES_INFO_GET,
SOF_IPC4_FW_PARAM_LIBRARIES_INFO_GET = 16,
SOF_IPC4_FW_PARAM_SYSTEM_TIME = 20,
+ SOF_IPC4_FW_PARAM_MIC_PRIVACY_STATE_CHANGE = 35,
};
enum sof_ipc4_fw_config_params {
@@ -446,6 +447,18 @@ struct sof_ipc4_dx_state_info {
uint32_t dx_mask;
} __packed __aligned(4);
+enum sof_ipc4_hw_config_params {
+ SOF_IPC4_HW_CFG_INTEL_MIC_PRIVACY_CAPS = 11,
+};
+
+#define SOF_IPC_INTEL_MIC_PRIVACY_VERSION_PTL 1
+
+struct sof_ipc4_intel_mic_privacy_cap {
+ uint32_t version;
+ uint32_t capabilities_length;
+ uint32_t capabilities[];
+} __packed;
+
/* Reply messages */
/*
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index f40daa616803..675f9fc92dde 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -34,7 +34,7 @@ snd-sof-pci-intel-icl-y := pci-icl.o icl.o
snd-sof-pci-intel-tgl-y := pci-tgl.o tgl.o
snd-sof-pci-intel-mtl-y := pci-mtl.o mtl.o
snd-sof-pci-intel-lnl-y := pci-lnl.o lnl.o
-snd-sof-pci-intel-ptl-y := pci-ptl.o
+snd-sof-pci-intel-ptl-y := pci-ptl.o ptl.o
obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o
obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index fe627bcb0531..ce561fe52bd5 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -16,6 +16,7 @@
#include <linux/bitfield.h>
#include <linux/module.h>
+#include <linux/string_choices.h>
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
@@ -42,6 +43,7 @@
* @shim_offset: offset to SHIM register base
* @ip_offset: offset to IP register base
* @shim_vs_offset: offset to vendor-specific (VS) SHIM base
+ * @mic_privacy_mask: bitmask of sublinks where mic privacy is applied
*/
struct hdac_ext2_link {
struct hdac_ext_link hext_link;
@@ -65,6 +67,8 @@ struct hdac_ext2_link {
u32 shim_offset;
u32 ip_offset;
u32 shim_vs_offset;
+
+ unsigned long mic_privacy_mask;
};
#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
@@ -90,6 +94,13 @@ struct hdac_ext2_link {
#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
+/* Microphone privacy */
+#define AZX_REG_INTEL_VS_SHIM_PVCCS 0x10
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE BIT(0)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG BIT(8)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS BIT(9)
+#define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS BIT(10)
+
/* HDAML section - this part follows sequences in the hardware specification,
* including naming conventions and the use of the hdaml_ prefix.
* The code is intentionally minimal with limited dependencies on frameworks or
@@ -696,6 +707,20 @@ static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid,
}
ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+ if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
+ u16 __iomem *pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ sublink * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+ u16 val = readw(pvccs);
+
+ writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
+
+ if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
+ dev_dbg(bus->dev,
+ "sublink %d (%d:%d): Mic privacy is enabled\n",
+ sublink, alt, elid);
+ }
skip_init:
if (eml_lock)
@@ -742,6 +767,16 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid
if (--h2link->sublink_ref_count[sublink] > 0)
goto skip_shutdown;
}
+
+ if (h2link->mic_privacy_mask & BIT(sublink)) {
+ u16 __iomem *pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ sublink * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
+ }
+
ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
skip_shutdown:
@@ -987,6 +1022,98 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e
}
EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
+void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
+ unsigned long mask)
+{
+ struct hdac_ext2_link *h2link;
+
+ if (!mask)
+ return;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (__fls(mask) > h2link->slcount) {
+ dev_warn(bus->dev,
+ "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
+ __func__, alt, elid, h2link->slcount, mask);
+ return;
+ }
+
+ dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
+ elid, h2link->slcount, mask);
+
+ h2link->mic_privacy_mask = mask;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ bool changed = false;
+ u16 __iomem *pvccs;
+ int i;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ /* The change in privacy state needs to be acked for each link */
+ for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
+ u16 val;
+
+ if (h2link->sublink_ref_count[i] == 0)
+ continue;
+
+ pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ i * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ val = readw(pvccs);
+ if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
+ writew(val, pvccs);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
+
+bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ u16 __iomem *pvccs;
+ bool state;
+ int i;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
+ if (h2link->sublink_ref_count[i] == 0)
+ continue;
+
+ /* Return the privacy state from the first active link */
+ pvccs = h2link->base_ptr +
+ h2link->shim_vs_offset +
+ i * h2link->instance_offset +
+ AZX_REG_INTEL_VS_SHIM_PVCCS;
+
+ state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
+ dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
+ elid, str_enabled_disabled(state));
+
+ return state;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
+
#endif
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index a1ccd95da8bb..6b1ada566476 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -352,6 +352,27 @@ void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(hda_sdw_process_wakeen_common, "SND_SOC_SOF_INTEL_HDA_GENERIC");
+static bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->check_mic_privacy_irq)
+ return chip->check_mic_privacy_irq(sdev, true,
+ AZX_REG_ML_LEPTR_ID_SDW);
+
+ return false;
+}
+
+static void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev)
+{
+ const struct sof_intel_dsp_desc *chip;
+
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->process_mic_privacy)
+ chip->process_mic_privacy(sdev, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+
#else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
{
@@ -383,6 +404,13 @@ static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
return false;
}
+static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
+{
+ return false;
+}
+
+static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { }
+
#endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
/* pre fw run operations */
@@ -678,7 +706,13 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
if (hda_dsp_check_sdw_irq(sdev)) {
trace_sof_intel_hda_irq(sdev, "sdw");
+
hda_dsp_sdw_thread(irq, hdev->sdw);
+
+ if (hda_dsp_sdw_check_mic_privacy_irq(sdev)) {
+ trace_sof_intel_hda_irq(sdev, "mic privacy");
+ hda_dsp_sdw_process_mic_privacy(sdev);
+ }
}
if (hda_sdw_check_wakeen_irq(sdev)) {
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index ee4ccc1a5490..76154627fc17 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -913,10 +913,6 @@ extern struct snd_sof_dsp_ops sof_tgl_ops;
int sof_tgl_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_icl_ops;
int sof_icl_ops_init(struct snd_sof_dev *sdev);
-extern struct snd_sof_dsp_ops sof_mtl_ops;
-int sof_mtl_ops_init(struct snd_sof_dev *sdev);
-extern struct snd_sof_dsp_ops sof_lnl_ops;
-int sof_lnl_ops_init(struct snd_sof_dev *sdev);
extern const struct sof_intel_dsp_desc skl_chip_info;
extern const struct sof_intel_dsp_desc apl_chip_info;
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
index 793d8539821d..2f3222040f98 100644
--- a/sound/soc/sof/intel/lnl.c
+++ b/sound/soc/sof/intel/lnl.c
@@ -20,17 +20,6 @@
#include "lnl.h"
#include <sound/hda-mlink.h>
-/* LunarLake ops */
-struct snd_sof_dsp_ops sof_lnl_ops;
-EXPORT_SYMBOL_NS(sof_lnl_ops, "SND_SOC_SOF_INTEL_LNL");
-
-static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = {
- {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS},
- {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS},
- {"fw_regs", HDA_DSP_BAR, MTL_SRAM_WINDOW_OFFSET(0), 0x1000, SOF_DEBUGFS_ACCESS_D0_ONLY},
-};
-
/* this helps allows the DSP to setup DMIC/SSP */
static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable)
{
@@ -111,94 +100,50 @@ static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
return 0;
}
-int sof_lnl_ops_init(struct snd_sof_dev *sdev)
+int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
{
- struct sof_ipc4_fw_data *ipc4_data;
+ int ret;
- /* common defaults */
- memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+ ret = sof_mtl_set_ops(sdev, dsp_ops);
+ if (ret)
+ return ret;
/* probe/remove */
if (!sdev->dspless_mode_selected) {
- sof_lnl_ops.probe = lnl_hda_dsp_probe;
- sof_lnl_ops.remove = lnl_hda_dsp_remove;
+ dsp_ops->probe = lnl_hda_dsp_probe;
+ dsp_ops->remove = lnl_hda_dsp_remove;
}
- /* shutdown */
- sof_lnl_ops.shutdown = hda_dsp_shutdown;
-
- /* doorbell */
- sof_lnl_ops.irq_thread = mtl_ipc_irq_thread;
-
- /* ipc */
- sof_lnl_ops.send_msg = mtl_ipc_send_msg;
- sof_lnl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset;
- sof_lnl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset;
-
- /* debug */
- sof_lnl_ops.debug_map = lnl_dsp_debugfs;
- sof_lnl_ops.debug_map_count = ARRAY_SIZE(lnl_dsp_debugfs);
- sof_lnl_ops.dbg_dump = mtl_dsp_dump;
- sof_lnl_ops.ipc_dump = mtl_ipc_dump;
-
- /* pre/post fw run */
- sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run;
- sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run;
-
- /* parse platform specific extended manifest */
- sof_lnl_ops.parse_platform_ext_manifest = NULL;
-
- /* dsp core get/put */
- /* TODO: add core_get and core_put */
+ /* post fw run */
+ dsp_ops->post_fw_run = lnl_dsp_post_fw_run;
/* PM */
if (!sdev->dspless_mode_selected) {
- sof_lnl_ops.resume = lnl_hda_dsp_resume;
- sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume;
+ dsp_ops->resume = lnl_hda_dsp_resume;
+ dsp_ops->runtime_resume = lnl_hda_dsp_runtime_resume;
}
- /* dsp core get/put */
- sof_lnl_ops.core_get = mtl_dsp_core_get;
- sof_lnl_ops.core_put = mtl_dsp_core_put;
-
- sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
- if (!sdev->private)
- return -ENOMEM;
-
- ipc4_data = sdev->private;
- ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
-
- ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
-
- ipc4_data->fw_context_save = true;
-
- /* External library loading support */
- ipc4_data->load_library = hda_dsp_ipc4_load_library;
-
- /* set DAI ops */
- hda_set_dai_drv_ops(sdev, &sof_lnl_ops);
-
- sof_lnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
-
return 0;
-};
-EXPORT_SYMBOL_NS(sof_lnl_ops_init, "SND_SOC_SOF_INTEL_LNL");
+}
+EXPORT_SYMBOL_NS(sof_lnl_set_ops, "SND_SOC_SOF_INTEL_LNL");
/* Check if an SDW IRQ occurred */
-static bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
+bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
}
+EXPORT_SYMBOL_NS(lnl_dsp_check_sdw_irq, "SND_SOC_SOF_INTEL_LNL");
-static int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
+int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
{
mtl_disable_ipc_interrupts(sdev);
return mtl_enable_interrupts(sdev, false);
}
+EXPORT_SYMBOL_NS(lnl_dsp_disable_interrupts, "SND_SOC_SOF_INTEL_LNL");
-static bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
+bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
u16 wake_sts;
@@ -214,6 +159,7 @@ static bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
/* filter out the range of SDIs that can be set for SoundWire */
return wake_sts & GENMASK(SDW_MAX_DEVICES, SDW_INTEL_DEV_NUM_IDA_MIN);
}
+EXPORT_SYMBOL_NS(lnl_sdw_check_wakeen_irq, "SND_SOC_SOF_INTEL_LNL");
const struct sof_intel_dsp_desc lnl_chip_info = {
.cores_num = 5,
@@ -239,26 +185,5 @@ const struct sof_intel_dsp_desc lnl_chip_info = {
.hw_ip_version = SOF_INTEL_ACE_2_0,
};
-const struct sof_intel_dsp_desc ptl_chip_info = {
- .cores_num = 5,
- .init_core_mask = BIT(0),
- .host_managed_cores_mask = BIT(0),
- .ipc_req = MTL_DSP_REG_HFIPCXIDR,
- .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
- .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
- .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
- .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
- .rom_status_reg = LNL_DSP_REG_HFDSC,
- .rom_init_timeout = 300,
- .ssp_count = MTL_SSP_COUNT,
- .d0i3_offset = MTL_HDA_VS_D0I3C,
- .read_sdw_lcount = hda_sdw_check_lcount_ext,
- .check_sdw_irq = lnl_dsp_check_sdw_irq,
- .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
- .check_ipc_irq = mtl_dsp_check_ipc_irq,
- .cl_init = mtl_dsp_cl_init,
- .power_down_dsp = mtl_power_down_dsp,
- .disable_interrupts = lnl_dsp_disable_interrupts,
- .hw_ip_version = SOF_INTEL_ACE_3_0,
-};
-EXPORT_SYMBOL_NS(ptl_chip_info, "SND_SOC_SOF_INTEL_LNL");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/lnl.h b/sound/soc/sof/intel/lnl.h
index 79101af84b2e..2837f818ac51 100644
--- a/sound/soc/sof/intel/lnl.h
+++ b/sound/soc/sof/intel/lnl.h
@@ -12,4 +12,10 @@
#define LNL_DSP_REG_HFDSC 0x160200 /* DSP core0 status */
#define LNL_DSP_REG_HFDEC 0x160204 /* DSP core0 error */
+int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
+
+bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev);
+int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev);
+bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev);
+
#endif /* __SOF_INTEL_LNL_H */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 4a6b1d56171e..2242c96bfa51 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -96,7 +96,7 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
return false;
}
-int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
struct sof_ipc4_msg *msg_data = msg->msg_data;
@@ -122,7 +122,6 @@ int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
-EXPORT_SYMBOL_NS(mtl_ipc_send_msg, "SND_SOC_SOF_INTEL_MTL");
void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
{
@@ -238,7 +237,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL");
/* pre fw run operations */
-int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
+static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
u32 dsphfpwrsts;
@@ -298,9 +297,8 @@ int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
return ret;
}
-EXPORT_SYMBOL_NS(mtl_dsp_pre_fw_run, "SND_SOC_SOF_INTEL_MTL");
-int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev)
{
int ret;
@@ -325,9 +323,8 @@ int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev)
hda_sdw_int_enable(sdev, true);
return 0;
}
-EXPORT_SYMBOL_NS(mtl_dsp_post_fw_run, "SND_SOC_SOF_INTEL_MTL");
-void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
+static void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
{
char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
u32 fwsts;
@@ -343,7 +340,6 @@ void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
sof_ipc4_intel_dump_telemetry_state(sdev, flags);
}
-EXPORT_SYMBOL_NS(mtl_dsp_dump, "SND_SOC_SOF_INTEL_MTL");
static bool mtl_dsp_primary_core_is_enabled(struct snd_sof_dev *sdev)
{
@@ -559,7 +555,7 @@ err:
}
EXPORT_SYMBOL_NS(mtl_dsp_cl_init, "SND_SOC_SOF_INTEL_MTL");
-irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
+static irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
{
struct sof_ipc4_msg notification_data = {{ 0 }};
struct snd_sof_dev *sdev = context;
@@ -641,21 +637,18 @@ irqreturn_t mtl_ipc_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
-EXPORT_SYMBOL_NS(mtl_ipc_irq_thread, "SND_SOC_SOF_INTEL_MTL");
-int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
+static int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MTL_DSP_MBOX_UPLINK_OFFSET;
}
-EXPORT_SYMBOL_NS(mtl_dsp_ipc_get_mailbox_offset, "SND_SOC_SOF_INTEL_MTL");
-int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+static int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
{
return MTL_SRAM_WINDOW_OFFSET(id);
}
-EXPORT_SYMBOL_NS(mtl_dsp_ipc_get_window_offset, "SND_SOC_SOF_INTEL_MTL");
-void mtl_ipc_dump(struct snd_sof_dev *sdev)
+static void mtl_ipc_dump(struct snd_sof_dev *sdev)
{
u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl;
@@ -671,7 +664,6 @@ void mtl_ipc_dump(struct snd_sof_dev *sdev)
"Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n",
hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl);
}
-EXPORT_SYMBOL_NS(mtl_ipc_dump, "SND_SOC_SOF_INTEL_MTL");
static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
{
@@ -680,7 +672,7 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
return mtl_enable_interrupts(sdev, false);
}
-int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
+static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
{
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
@@ -692,9 +684,8 @@ int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core)
return 0;
}
-EXPORT_SYMBOL_NS(mtl_dsp_core_get, "SND_SOC_SOF_INTEL_MTL");
-int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
+static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
{
const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
int ret;
@@ -710,45 +701,41 @@ int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core)
return 0;
}
-EXPORT_SYMBOL_NS(mtl_dsp_core_put, "SND_SOC_SOF_INTEL_MTL");
-/* Meteorlake ops */
-struct snd_sof_dsp_ops sof_mtl_ops;
-
-int sof_mtl_ops_init(struct snd_sof_dev *sdev)
+int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
{
struct sof_ipc4_fw_data *ipc4_data;
/* common defaults */
- memcpy(&sof_mtl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
+ memcpy(dsp_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
/* shutdown */
- sof_mtl_ops.shutdown = hda_dsp_shutdown;
+ dsp_ops->shutdown = hda_dsp_shutdown;
/* doorbell */
- sof_mtl_ops.irq_thread = mtl_ipc_irq_thread;
+ dsp_ops->irq_thread = mtl_ipc_irq_thread;
/* ipc */
- sof_mtl_ops.send_msg = mtl_ipc_send_msg;
- sof_mtl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset;
- sof_mtl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset;
+ dsp_ops->send_msg = mtl_ipc_send_msg;
+ dsp_ops->get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset;
+ dsp_ops->get_window_offset = mtl_dsp_ipc_get_window_offset;
/* debug */
- sof_mtl_ops.debug_map = mtl_dsp_debugfs;
- sof_mtl_ops.debug_map_count = ARRAY_SIZE(mtl_dsp_debugfs);
- sof_mtl_ops.dbg_dump = mtl_dsp_dump;
- sof_mtl_ops.ipc_dump = mtl_ipc_dump;
+ dsp_ops->debug_map = mtl_dsp_debugfs;
+ dsp_ops->debug_map_count = ARRAY_SIZE(mtl_dsp_debugfs);
+ dsp_ops->dbg_dump = mtl_dsp_dump;
+ dsp_ops->ipc_dump = mtl_ipc_dump;
/* pre/post fw run */
- sof_mtl_ops.pre_fw_run = mtl_dsp_pre_fw_run;
- sof_mtl_ops.post_fw_run = mtl_dsp_post_fw_run;
+ dsp_ops->pre_fw_run = mtl_dsp_pre_fw_run;
+ dsp_ops->post_fw_run = mtl_dsp_post_fw_run;
/* parse platform specific extended manifest */
- sof_mtl_ops.parse_platform_ext_manifest = NULL;
+ dsp_ops->parse_platform_ext_manifest = NULL;
/* dsp core get/put */
- sof_mtl_ops.core_get = mtl_dsp_core_get;
- sof_mtl_ops.core_put = mtl_dsp_core_put;
+ dsp_ops->core_get = mtl_dsp_core_get;
+ dsp_ops->core_put = mtl_dsp_core_put;
sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
@@ -764,13 +751,14 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
/* External library loading support */
ipc4_data->load_library = hda_dsp_ipc4_load_library;
- /* set DAI ops */
- hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
+ dsp_ops->set_power_state = hda_dsp_set_power_state_ipc4;
- sof_mtl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+ /* set DAI ops */
+ hda_set_dai_drv_ops(sdev, dsp_ops);
return 0;
-};
+}
+EXPORT_SYMBOL_NS(sof_mtl_set_ops, "SND_SOC_SOF_INTEL_MTL");
const struct sof_intel_dsp_desc mtl_chip_info = {
.cores_num = 3,
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 9ab4b21c960e..e01a1536709e 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -122,26 +122,13 @@
#define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0)
bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
-int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev);
void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev);
int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable);
-int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev);
-int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev);
-void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags);
-
int mtl_power_down_dsp(struct snd_sof_dev *sdev);
int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
-irqreturn_t mtl_ipc_irq_thread(int irq, void *context);
-
-int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev);
-int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
-
-void mtl_ipc_dump(struct snd_sof_dev *sdev);
-
-int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core);
-int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core);
+int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c
index 8d4d74ac4398..91b9cff8c4ca 100644
--- a/sound/soc/sof/intel/pci-lnl.c
+++ b/sound/soc/sof/intel/pci-lnl.c
@@ -18,7 +18,15 @@
/* platform specific devices */
#include "hda.h"
-#include "mtl.h"
+#include "lnl.h"
+
+/* LunarLake ops */
+static struct snd_sof_dsp_ops sof_lnl_ops;
+
+static int sof_lnl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_lnl_set_ops(sdev, &sof_lnl_ops);
+}
static const struct sof_dev_desc lnl_desc = {
.use_acpi_target_states = true,
@@ -73,6 +81,4 @@ MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SOF support for LunarLake platforms");
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
-MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
-MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c
index 71f711cf8599..c5048a4a56be 100644
--- a/sound/soc/sof/intel/pci-mtl.c
+++ b/sound/soc/sof/intel/pci-mtl.c
@@ -20,6 +20,14 @@
#include "hda.h"
#include "mtl.h"
+/* Meteorlake ops */
+static struct snd_sof_dsp_ops sof_mtl_ops;
+
+static int sof_mtl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_mtl_set_ops(sdev, &sof_mtl_ops);
+}
+
static const struct sof_dev_desc mtl_desc = {
.use_acpi_target_states = true,
.machines = snd_soc_acpi_intel_mtl_machines,
diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c
index c4fb6a2441b7..3812ab6d99c0 100644
--- a/sound/soc/sof/intel/pci-ptl.c
+++ b/sound/soc/sof/intel/pci-ptl.c
@@ -16,7 +16,15 @@
/* platform specific devices */
#include "hda.h"
-#include "mtl.h"
+#include "ptl.h"
+
+/* PantherLake ops */
+static struct snd_sof_dsp_ops sof_ptl_ops;
+
+static int sof_ptl_ops_init(struct snd_sof_dev *sdev)
+{
+ return sof_ptl_set_ops(sdev, &sof_ptl_ops);
+}
static const struct sof_dev_desc ptl_desc = {
.use_acpi_target_states = true,
@@ -43,8 +51,8 @@ static const struct sof_dev_desc ptl_desc = {
[SOF_IPC_TYPE_4] = "sof-ptl.ri",
},
.nocodec_tplg_filename = "sof-ptl-nocodec.tplg",
- .ops = &sof_lnl_ops,
- .ops_init = sof_lnl_ops_init,
+ .ops = &sof_ptl_ops,
+ .ops_init = sof_ptl_ops_init,
};
/* PCI IDs */
@@ -72,7 +80,4 @@ MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SOF support for PantherLake platforms");
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC");
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON");
-MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL");
-MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
-MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/intel/ptl.c b/sound/soc/sof/intel/ptl.c
new file mode 100644
index 000000000000..8fa4bdceedd9
--- /dev/null
+++ b/sound/soc/sof/intel/ptl.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright(c) 2025 Intel Corporation
+
+/*
+ * Hardware interface for audio DSP on PantherLake.
+ */
+
+#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
+#include <sound/sof/ipc4/header.h>
+#include "../ipc4-priv.h"
+#include "../ops.h"
+#include "hda.h"
+#include "hda-ipc.h"
+#include "../sof-audio.h"
+#include "mtl.h"
+#include "lnl.h"
+#include "ptl.h"
+
+static bool sof_ptl_check_mic_privacy_irq(struct snd_sof_dev *sdev, bool alt,
+ int elid)
+{
+ if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW)
+ return false;
+
+ return hdac_bus_eml_is_mic_privacy_changed(sof_to_bus(sdev), alt, elid);
+}
+
+static void sof_ptl_process_mic_privacy(struct snd_sof_dev *sdev, bool alt,
+ int elid)
+{
+ bool state;
+
+ if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW)
+ return;
+
+ state = hdac_bus_eml_get_mic_privacy_state(sof_to_bus(sdev), alt, elid);
+
+ sof_ipc4_mic_privacy_state_change(sdev, state);
+}
+
+static void sof_ptl_set_mic_privacy(struct snd_sof_dev *sdev,
+ struct sof_ipc4_intel_mic_privacy_cap *caps)
+{
+ u32 micpvcp;
+
+ if (!caps || !caps->capabilities_length)
+ return;
+
+ micpvcp = caps->capabilities[0];
+
+ /* No need to set the mic privacy if it is not enabled or forced */
+ if (!(micpvcp & PTL_MICPVCP_DDZE_ENABLED) ||
+ micpvcp & PTL_MICPVCP_DDZE_FORCED)
+ return;
+
+ hdac_bus_eml_set_mic_privacy_mask(sof_to_bus(sdev), true,
+ AZX_REG_ML_LEPTR_ID_SDW,
+ PTL_MICPVCP_GET_SDW_MASK(micpvcp));
+}
+
+int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
+{
+ struct sof_ipc4_fw_data *ipc4_data;
+ int ret;
+
+ ret = sof_lnl_set_ops(sdev, dsp_ops);
+ if (ret)
+ return ret;
+
+ ipc4_data = sdev->private;
+ ipc4_data->intel_configure_mic_privacy = sof_ptl_set_mic_privacy;
+
+ return 0;
+};
+EXPORT_SYMBOL_NS(sof_ptl_set_ops, "SND_SOC_SOF_INTEL_PTL");
+
+const struct sof_intel_dsp_desc ptl_chip_info = {
+ .cores_num = 5,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .check_mic_privacy_irq = sof_ptl_check_mic_privacy_irq,
+ .process_mic_privacy = sof_ptl_process_mic_privacy,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_3_0,
+};
+
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
+MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL");
+MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/ptl.h b/sound/soc/sof/intel/ptl.h
new file mode 100644
index 000000000000..6a7ef11f411e
--- /dev/null
+++ b/sound/soc/sof/intel/ptl.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef __SOF_INTEL_PTL_H
+#define __SOF_INTEL_PTL_H
+
+#define PTL_MICPVCP_DDZE_FORCED BIT(16)
+#define PTL_MICPVCP_DDZE_ENABLED BIT(17)
+#define PTL_MICPVCP_DDZLS_SDW GENMASK(26, 20)
+#define PTL_MICPVCP_GET_SDW_MASK(x) (((x) & PTL_MICPVCP_DDZLS_SDW) >> 20)
+
+int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops);
+
+#endif /* __SOF_INTEL_PTL_H */
diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h
index 8709b750a11e..d4372f0bff7e 100644
--- a/sound/soc/sof/intel/shim.h
+++ b/sound/soc/sof/intel/shim.h
@@ -193,6 +193,8 @@ struct sof_intel_dsp_desc {
bool (*check_sdw_wakeen_irq)(struct snd_sof_dev *sdev);
void (*sdw_process_wakeen)(struct snd_sof_dev *sdev);
bool (*check_ipc_irq)(struct snd_sof_dev *sdev);
+ bool (*check_mic_privacy_irq)(struct snd_sof_dev *sdev, bool alt, int elid);
+ void (*process_mic_privacy)(struct snd_sof_dev *sdev, bool alt, int elid);
int (*power_down_dsp)(struct snd_sof_dev *sdev);
int (*disable_interrupts)(struct snd_sof_dev *sdev);
int (*cl_init)(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index 6ad1ef0e53e8..d2f534d65edf 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -502,6 +502,39 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
offset += sizeof(*tuple) + tuple->size;
}
+ /* Get the hardware configuration */
+ msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_HW_CONFIG_GET);
+
+ msg.data_size = sdev->ipc->max_payload_size;
+
+ ret = iops->set_get_data(sdev, &msg, msg.data_size, false);
+ if (ret)
+ goto out;
+
+ offset = 0;
+ while (offset < msg.data_size) {
+ tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset);
+
+ switch (tuple->type) {
+ case SOF_IPC4_HW_CFG_INTEL_MIC_PRIVACY_CAPS:
+ if (ipc4_data->intel_configure_mic_privacy) {
+ struct sof_ipc4_intel_mic_privacy_cap *caps;
+
+ caps = (struct sof_ipc4_intel_mic_privacy_cap *)tuple->value;
+ ipc4_data->intel_configure_mic_privacy(sdev, caps);
+ }
+ break;
+ default:
+ break;
+ }
+
+ offset += sizeof(*tuple) + tuple->size;
+ }
+
out:
kfree(msg.data_ptr);
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index b798810eff91..58b032820683 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -11,6 +11,7 @@
#include <linux/idr.h>
#include <sound/sof/ext_manifest4.h>
+#include <sound/sof/ipc4/header.h>
#include "sof-priv.h"
/* The DSP window indices are fixed */
@@ -89,6 +90,8 @@ struct sof_ipc4_fw_data {
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
+ void (*intel_configure_mic_privacy)(struct snd_sof_dev *sdev,
+ struct sof_ipc4_intel_mic_privacy_cap *caps);
struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
};
@@ -118,4 +121,6 @@ void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev,
size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev,
u32 slot_type);
+void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state);
+
#endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 2ed0c52fb2f1..37e837b22ac8 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -851,3 +851,21 @@ const struct sof_ipc_ops ipc4_ops = {
.pcm = &ipc4_pcm_ops,
.fw_tracing = &ipc4_mtrace_ops,
};
+
+void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state)
+{
+ struct sof_ipc4_msg msg;
+ u32 data = state;
+
+ msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID);
+ msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_MIC_PRIVACY_STATE_CHANGE);
+
+ msg.data_size = sizeof(data);
+ msg.data_ptr = &data;
+
+ sof_ipc4_set_get_data(sdev, &msg, msg.data_size, true);
+}
+EXPORT_SYMBOL(sof_ipc4_mic_privacy_state_change);