Skip to content

Commit 8a7e7a0

Browse files
committed
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.
2 parents 1ec3f1d + 4a43c32 commit 8a7e7a0

19 files changed

Lines changed: 470 additions & 167 deletions

File tree

include/sound/hda-mlink.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid);
6262

6363
int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable);
6464

65+
/* microphone privacy specific function supported by ACE3+ architecture */
66+
void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
67+
unsigned long mask);
68+
bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid);
69+
bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid);
70+
6571
#else
6672

6773
static inline int
@@ -185,4 +191,23 @@ hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enabl
185191
{
186192
return 0;
187193
}
194+
195+
static inline void
196+
hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
197+
unsigned long mask)
198+
{
199+
}
200+
201+
static inline bool
202+
hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
203+
{
204+
return false;
205+
}
206+
207+
static inline bool
208+
hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
209+
{
210+
return false;
211+
}
212+
188213
#endif /* CONFIG_SND_SOC_SOF_HDA_MLINK */

include/sound/sof/ipc4/header.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ enum sof_ipc4_base_fw_params {
396396
SOF_IPC4_FW_PARAM_MODULES_INFO_GET,
397397
SOF_IPC4_FW_PARAM_LIBRARIES_INFO_GET = 16,
398398
SOF_IPC4_FW_PARAM_SYSTEM_TIME = 20,
399+
SOF_IPC4_FW_PARAM_MIC_PRIVACY_STATE_CHANGE = 35,
399400
};
400401

401402
enum sof_ipc4_fw_config_params {
@@ -446,6 +447,18 @@ struct sof_ipc4_dx_state_info {
446447
uint32_t dx_mask;
447448
} __packed __aligned(4);
448449

450+
enum sof_ipc4_hw_config_params {
451+
SOF_IPC4_HW_CFG_INTEL_MIC_PRIVACY_CAPS = 11,
452+
};
453+
454+
#define SOF_IPC_INTEL_MIC_PRIVACY_VERSION_PTL 1
455+
456+
struct sof_ipc4_intel_mic_privacy_cap {
457+
uint32_t version;
458+
uint32_t capabilities_length;
459+
uint32_t capabilities[];
460+
} __packed;
461+
449462
/* Reply messages */
450463

451464
/*

sound/soc/sof/intel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ snd-sof-pci-intel-icl-y := pci-icl.o icl.o
3434
snd-sof-pci-intel-tgl-y := pci-tgl.o tgl.o
3535
snd-sof-pci-intel-mtl-y := pci-mtl.o mtl.o
3636
snd-sof-pci-intel-lnl-y := pci-lnl.o lnl.o
37-
snd-sof-pci-intel-ptl-y := pci-ptl.o
37+
snd-sof-pci-intel-ptl-y := pci-ptl.o ptl.o
3838

3939
obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o
4040
obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o

sound/soc/sof/intel/hda-mlink.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <linux/bitfield.h>
1818
#include <linux/module.h>
19+
#include <linux/string_choices.h>
1920

2021
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
2122

@@ -42,6 +43,7 @@
4243
* @shim_offset: offset to SHIM register base
4344
* @ip_offset: offset to IP register base
4445
* @shim_vs_offset: offset to vendor-specific (VS) SHIM base
46+
* @mic_privacy_mask: bitmask of sublinks where mic privacy is applied
4547
*/
4648
struct hdac_ext2_link {
4749
struct hdac_ext_link hext_link;
@@ -65,6 +67,8 @@ struct hdac_ext2_link {
6567
u32 shim_offset;
6668
u32 ip_offset;
6769
u32 shim_vs_offset;
70+
71+
unsigned long mic_privacy_mask;
6872
};
6973

7074
#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
@@ -90,6 +94,13 @@ struct hdac_ext2_link {
9094
#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
9195
#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
9296

97+
/* Microphone privacy */
98+
#define AZX_REG_INTEL_VS_SHIM_PVCCS 0x10
99+
#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE BIT(0)
100+
#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG BIT(8)
101+
#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS BIT(9)
102+
#define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS BIT(10)
103+
93104
/* HDAML section - this part follows sequences in the hardware specification,
94105
* including naming conventions and the use of the hdaml_ prefix.
95106
* 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,
696707
}
697708

698709
ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
710+
if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) {
711+
u16 __iomem *pvccs = h2link->base_ptr +
712+
h2link->shim_vs_offset +
713+
sublink * h2link->instance_offset +
714+
AZX_REG_INTEL_VS_SHIM_PVCCS;
715+
u16 val = readw(pvccs);
716+
717+
writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
718+
719+
if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS)
720+
dev_dbg(bus->dev,
721+
"sublink %d (%d:%d): Mic privacy is enabled\n",
722+
sublink, alt, elid);
723+
}
699724

700725
skip_init:
701726
if (eml_lock)
@@ -742,6 +767,16 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid
742767
if (--h2link->sublink_ref_count[sublink] > 0)
743768
goto skip_shutdown;
744769
}
770+
771+
if (h2link->mic_privacy_mask & BIT(sublink)) {
772+
u16 __iomem *pvccs = h2link->base_ptr +
773+
h2link->shim_vs_offset +
774+
sublink * h2link->instance_offset +
775+
AZX_REG_INTEL_VS_SHIM_PVCCS;
776+
777+
writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs);
778+
}
779+
745780
ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
746781

747782
skip_shutdown:
@@ -987,6 +1022,98 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e
9871022
}
9881023
EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
9891024

1025+
void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid,
1026+
unsigned long mask)
1027+
{
1028+
struct hdac_ext2_link *h2link;
1029+
1030+
if (!mask)
1031+
return;
1032+
1033+
h2link = find_ext2_link(bus, alt, elid);
1034+
if (!h2link)
1035+
return;
1036+
1037+
if (__fls(mask) > h2link->slcount) {
1038+
dev_warn(bus->dev,
1039+
"%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n",
1040+
__func__, alt, elid, h2link->slcount, mask);
1041+
return;
1042+
}
1043+
1044+
dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt,
1045+
elid, h2link->slcount, mask);
1046+
1047+
h2link->mic_privacy_mask = mask;
1048+
}
1049+
EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK");
1050+
1051+
bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid)
1052+
{
1053+
struct hdac_ext2_link *h2link;
1054+
bool changed = false;
1055+
u16 __iomem *pvccs;
1056+
int i;
1057+
1058+
h2link = find_ext2_link(bus, alt, elid);
1059+
if (!h2link)
1060+
return false;
1061+
1062+
/* The change in privacy state needs to be acked for each link */
1063+
for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1064+
u16 val;
1065+
1066+
if (h2link->sublink_ref_count[i] == 0)
1067+
continue;
1068+
1069+
pvccs = h2link->base_ptr +
1070+
h2link->shim_vs_offset +
1071+
i * h2link->instance_offset +
1072+
AZX_REG_INTEL_VS_SHIM_PVCCS;
1073+
1074+
val = readw(pvccs);
1075+
if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) {
1076+
writew(val, pvccs);
1077+
changed = true;
1078+
}
1079+
}
1080+
1081+
return changed;
1082+
}
1083+
EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK");
1084+
1085+
bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid)
1086+
{
1087+
struct hdac_ext2_link *h2link;
1088+
u16 __iomem *pvccs;
1089+
bool state;
1090+
int i;
1091+
1092+
h2link = find_ext2_link(bus, alt, elid);
1093+
if (!h2link)
1094+
return false;
1095+
1096+
for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) {
1097+
if (h2link->sublink_ref_count[i] == 0)
1098+
continue;
1099+
1100+
/* Return the privacy state from the first active link */
1101+
pvccs = h2link->base_ptr +
1102+
h2link->shim_vs_offset +
1103+
i * h2link->instance_offset +
1104+
AZX_REG_INTEL_VS_SHIM_PVCCS;
1105+
1106+
state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS;
1107+
dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt,
1108+
elid, str_enabled_disabled(state));
1109+
1110+
return state;
1111+
}
1112+
1113+
return false;
1114+
}
1115+
EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK");
1116+
9901117
#endif
9911118

9921119
MODULE_LICENSE("Dual BSD/GPL");

sound/soc/sof/intel/hda.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,27 @@ void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev)
352352
}
353353
EXPORT_SYMBOL_NS(hda_sdw_process_wakeen_common, "SND_SOC_SOF_INTEL_HDA_GENERIC");
354354

355+
static bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
356+
{
357+
const struct sof_intel_dsp_desc *chip;
358+
359+
chip = get_chip_info(sdev->pdata);
360+
if (chip && chip->check_mic_privacy_irq)
361+
return chip->check_mic_privacy_irq(sdev, true,
362+
AZX_REG_ML_LEPTR_ID_SDW);
363+
364+
return false;
365+
}
366+
367+
static void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev)
368+
{
369+
const struct sof_intel_dsp_desc *chip;
370+
371+
chip = get_chip_info(sdev->pdata);
372+
if (chip && chip->process_mic_privacy)
373+
chip->process_mic_privacy(sdev, true, AZX_REG_ML_LEPTR_ID_SDW);
374+
}
375+
355376
#else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
356377
static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
357378
{
@@ -383,6 +404,13 @@ static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
383404
return false;
384405
}
385406

407+
static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev)
408+
{
409+
return false;
410+
}
411+
412+
static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { }
413+
386414
#endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */
387415

388416
/* pre fw run operations */
@@ -678,7 +706,13 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
678706

679707
if (hda_dsp_check_sdw_irq(sdev)) {
680708
trace_sof_intel_hda_irq(sdev, "sdw");
709+
681710
hda_dsp_sdw_thread(irq, hdev->sdw);
711+
712+
if (hda_dsp_sdw_check_mic_privacy_irq(sdev)) {
713+
trace_sof_intel_hda_irq(sdev, "mic privacy");
714+
hda_dsp_sdw_process_mic_privacy(sdev);
715+
}
682716
}
683717

684718
if (hda_sdw_check_wakeen_irq(sdev)) {

sound/soc/sof/intel/hda.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -913,10 +913,6 @@ extern struct snd_sof_dsp_ops sof_tgl_ops;
913913
int sof_tgl_ops_init(struct snd_sof_dev *sdev);
914914
extern struct snd_sof_dsp_ops sof_icl_ops;
915915
int sof_icl_ops_init(struct snd_sof_dev *sdev);
916-
extern struct snd_sof_dsp_ops sof_mtl_ops;
917-
int sof_mtl_ops_init(struct snd_sof_dev *sdev);
918-
extern struct snd_sof_dsp_ops sof_lnl_ops;
919-
int sof_lnl_ops_init(struct snd_sof_dev *sdev);
920916

921917
extern const struct sof_intel_dsp_desc skl_chip_info;
922918
extern const struct sof_intel_dsp_desc apl_chip_info;

0 commit comments

Comments
 (0)