Skip to content

Commit

Permalink
ASoC: SOF: hda/ptl: Move mic privacy change notification sending to a…
Browse files Browse the repository at this point in the history
… work

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: 4a43c32 ("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>
  • Loading branch information
Peter Ujfalusi authored and Mark Brown committed Mar 31, 2025
1 parent 7eccc86 commit 7d783d9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 4 deletions.
8 changes: 8 additions & 0 deletions sound/soc/sof/intel/hda-dsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions sound/soc/sof/intel/hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions sound/soc/sof/intel/hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
33 changes: 29 additions & 4 deletions sound/soc/sof/intel/ptl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit 7d783d9

Please sign in to comment.