Unverified Commit f81dc982 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: Intel/ipc4: Support for low power playback

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The following series will enable the the Low Power Audio (LPA)
playback on Intel platforms when using IPC4.

The support is closely follows how IPC3 supports similar use case.

All depending patches are upstream and our CI have been testing
this feature for some time without issues.
parents ec285cb9 6611b975
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -280,6 +280,8 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
	snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
			  msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);

	hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);

	return 0;
}

+35 −11
Original line number Diff line number Diff line
@@ -364,19 +364,12 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)

static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
{
	struct sof_ipc_pm_gate pm_gate;
	struct sof_ipc_reply reply;
	const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);

	memset(&pm_gate, 0, sizeof(pm_gate));
	if (pm_ops && pm_ops->set_pm_gate)
		return pm_ops->set_pm_gate(sdev, flags);

	/* configure pm_gate ipc message */
	pm_gate.hdr.size = sizeof(pm_gate);
	pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
	pm_gate.flags = flags;

	/* send pm_gate ipc to dsp */
	return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
					&reply, sizeof(reply));
	return 0;
}

static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
@@ -412,6 +405,34 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
	return 0;
}

/*
 * d0i3 streaming is enabled if all the active streams can
 * work in d0i3 state and playback is enabled
 */
static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev)
{
	struct snd_pcm_substream *substream;
	struct snd_sof_pcm *spcm;
	bool playback_active = false;
	int dir;

	list_for_each_entry(spcm, &sdev->pcm_list, list) {
		for_each_pcm_streams(dir) {
			substream = spcm->stream[dir].substream;
			if (!substream || !substream->runtime)
				continue;

			if (!spcm->stream[dir].d0i3_compatible)
				return false;

			if (dir == SNDRV_PCM_STREAM_PLAYBACK)
				playback_active = true;
		}
	}

	return playback_active;
}

static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
				const struct sof_dsp_power_state *target_state)
{
@@ -453,6 +474,9 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
		    !hda_enable_trace_D0I3_S0 ||
		    sdev->system_suspend_target != SOF_SUSPEND_NONE)
			flags = HDA_PM_NO_DMA_TRACE;

		if (hda_dsp_d0i3_streaming_applicable(sdev))
			flags |= HDA_PM_PG_STREAMING;
	} else {
		/* prevent power gating in D0I0 */
		flags = HDA_PM_PPG;
+28 −0
Original line number Diff line number Diff line
@@ -67,6 +67,32 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
	return 0;
}

static inline bool hda_dsp_ipc4_pm_msg(u32 primary)
{
	/* pm setting is only supported by module msg */
	if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)
		return false;

	if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||
	    SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)
		return true;

	return false;
}

void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
				     struct snd_sof_ipc_msg *msg)
{
	struct sof_ipc4_msg *msg_data = msg->msg_data;

	/* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */
	if (hda_dsp_ipc4_pm_msg(msg_data->primary))
		return;

	mod_delayed_work(system_wq, &hdev->d0i3_work,
			 msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
}

int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
	struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
@@ -88,6 +114,8 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
	snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
			  msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);

	hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);

	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -919,6 +919,8 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev);
void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
				     struct snd_sof_ipc_msg *msg);
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
void hda_ipc4_dump(struct snd_sof_dev *sdev);
extern struct sdw_intel_ops sdw_callback;
+2 −0
Original line number Diff line number Diff line
@@ -110,6 +110,8 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms
	snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR,
			  msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY);

	hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);

	return 0;
}

Loading