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
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 */
4648struct 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
700725skip_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
747782skip_shutdown :
@@ -987,6 +1022,98 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e
9871022}
9881023EXPORT_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
9921119MODULE_LICENSE ("Dual BSD/GPL" );
0 commit comments