ASoC: SOF: hda/ptl: Move mic privacy change notification sending to a work
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Mon, 31 Mar 2025 07:06:23 +0000 (10:06 +0300)
committerMark Brown <broonie@kernel.org>
Mon, 31 Mar 2025 11:51:55 +0000 (12:51 +0100)
IPC message cannot be sent from the irq thread directly as the message will
not receive the reply (interrupts are disabled) and it will time out - the
reply is going to be received right after the we leave the irq thread.
This is a different case compared to the delayed IPC messages due to DSP
busy state.

Add support for sending the mic privacy change notification to the firmware
from a work instead of the process callback.

The work needs to be canceled if there is a chance that it might be running
on module remove or before system/runtime suspend.

Fixes: 4a43c3241ec3 ("ASoC: SOF: Intel: ptl: Add support for mic privacy")
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://patch.msgid.link/20250331070623.5985-1-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda.c
sound/soc/sof/intel/hda.h
sound/soc/sof/intel/ptl.c

index ccf8eefdca707ca87d561d4a69c171453e6d8974..f64e8a6a9a33c258b01975f8db345d70c7da0e40 100644 (file)
@@ -991,6 +991,10 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
        if (!sdev->dspless_mode_selected) {
                /* cancel any attempt for DSP D0I3 */
                cancel_delayed_work_sync(&hda->d0i3_work);
+
+               /* Cancel the microphone privacy work if mic privacy is active */
+               if (hda->mic_privacy.active)
+                       cancel_work_sync(&hda->mic_privacy.work);
        }
 
        /* stop hda controller and power dsp off */
@@ -1017,6 +1021,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
        if (!sdev->dspless_mode_selected) {
                /* cancel any attempt for DSP D0I3 */
                cancel_delayed_work_sync(&hda->d0i3_work);
+
+               /* Cancel the microphone privacy work if mic privacy is active */
+               if (hda->mic_privacy.active)
+                       cancel_work_sync(&hda->mic_privacy.work);
        }
 
        if (target_state == SOF_DSP_PM_D0) {
index 6b1ada5664764fbcc45136a1b6852296a0a89194..b34e5fdf10f16f6a90c5b72e4171452a196cbe23 100644 (file)
@@ -968,6 +968,10 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
        if (sdev->dspless_mode_selected)
                goto skip_disable_dsp;
 
+       /* Cancel the microphone privacy work if mic privacy is active */
+       if (hda->mic_privacy.active)
+               cancel_work_sync(&hda->mic_privacy.work);
+
        /* no need to check for error as the DSP will be disabled anyway */
        if (chip && chip->power_down_dsp)
                chip->power_down_dsp(sdev);
index 76154627fc174744c5c3e849254cb02699614604..108cad04879eaceed64c7224a33b787699086004 100644 (file)
@@ -487,6 +487,11 @@ enum sof_hda_D0_substate {
        SOF_HDA_DSP_PM_D0I3,    /* low power D0 substate */
 };
 
+struct sof_ace3_mic_privacy {
+       bool active;
+       struct work_struct work;
+};
+
 /* represents DSP HDA controller frontend - i.e. host facing control */
 struct sof_intel_hda_dev {
        bool imrboot_supported;
@@ -542,6 +547,9 @@ struct sof_intel_hda_dev {
        /* Intel NHLT information */
        struct nhlt_acpi_table *nhlt;
 
+       /* work queue for mic privacy state change notification sending */
+       struct sof_ace3_mic_privacy mic_privacy;
+
        /*
         * Pointing to the IPC message if immediate sending was not possible
         * because the downlink communication channel was BUSY at the time.
index 8fa4bdceedd9ef411488f843c6aa907876772c8b..aa0b772178bcb02590dc3a74894dafa0c3d4bed8 100644 (file)
@@ -27,22 +27,44 @@ static bool sof_ptl_check_mic_privacy_irq(struct snd_sof_dev *sdev, bool alt,
        return hdac_bus_eml_is_mic_privacy_changed(sof_to_bus(sdev), alt, elid);
 }
 
+static void sof_ptl_mic_privacy_work(struct work_struct *work)
+{
+       struct sof_intel_hda_dev *hdev = container_of(work,
+                                                     struct sof_intel_hda_dev,
+                                                     mic_privacy.work);
+       struct hdac_bus *bus = &hdev->hbus.core;
+       struct snd_sof_dev *sdev = dev_get_drvdata(bus->dev);
+       bool state;
+
+       /*
+        * The microphone privacy state is only available via Soundwire shim
+        * in PTL
+        * The work is only scheduled on change.
+        */
+       state = hdac_bus_eml_get_mic_privacy_state(bus, 1,
+                                                  AZX_REG_ML_LEPTR_ID_SDW);
+       sof_ipc4_mic_privacy_state_change(sdev, state);
+}
+
 static void sof_ptl_process_mic_privacy(struct snd_sof_dev *sdev, bool alt,
                                        int elid)
 {
-       bool state;
+       struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
 
        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);
+       /*
+        * Schedule the work to read the microphone privacy state and send IPC
+        * message about the new state to the firmware
+        */
+       schedule_work(&hdev->mic_privacy.work);
 }
 
 static void sof_ptl_set_mic_privacy(struct snd_sof_dev *sdev,
                                    struct sof_ipc4_intel_mic_privacy_cap *caps)
 {
+       struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
        u32 micpvcp;
 
        if (!caps || !caps->capabilities_length)
@@ -58,6 +80,9 @@ static void sof_ptl_set_mic_privacy(struct snd_sof_dev *sdev,
        hdac_bus_eml_set_mic_privacy_mask(sof_to_bus(sdev), true,
                                          AZX_REG_ML_LEPTR_ID_SDW,
                                          PTL_MICPVCP_GET_SDW_MASK(micpvcp));
+
+       INIT_WORK(&hdev->mic_privacy.work, sof_ptl_mic_privacy_work);
+       hdev->mic_privacy.active = true;
 }
 
 int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)