Unverified Commit 7d783d90 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown
Browse files

ASoC: SOF: hda/ptl: Move mic privacy change notification sending to a 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: 4a43c324 ("ASoC: SOF: Intel: ptl: Add support for mic privacy")
Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarGuennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: default avatarLiam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://patch.msgid.link/20250331070623.5985-1-peter.ujfalusi@linux.intel.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 7eccc86e
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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) {
+4 −0
Original line number Diff line number Diff line
@@ -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);
+8 −0
Original line number Diff line number Diff line
@@ -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.
+29 −4
Original line number Diff line number Diff line
@@ -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)