Skip to content

Commit f81dc98

Browse files
committed
ASoC: SOF: Intel/ipc4: Support for low power playback
Merge series from Peter Ujfalusi <[email protected]>: 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.
2 parents ec285cb + 6611b97 commit f81dc98

File tree

8 files changed

+113
-11
lines changed

8 files changed

+113
-11
lines changed

sound/soc/sof/intel/cnl.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
280280
snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
281281
msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
282282

283+
hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
284+
283285
return 0;
284286
}
285287

sound/soc/sof/intel/hda-dsp.c

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,12 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
364364

365365
static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
366366
{
367-
struct sof_ipc_pm_gate pm_gate;
368-
struct sof_ipc_reply reply;
367+
const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
369368

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

372-
/* configure pm_gate ipc message */
373-
pm_gate.hdr.size = sizeof(pm_gate);
374-
pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
375-
pm_gate.flags = flags;
376-
377-
/* send pm_gate ipc to dsp */
378-
return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
379-
&reply, sizeof(reply));
372+
return 0;
380373
}
381374

382375
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)
412405
return 0;
413406
}
414407

408+
/*
409+
* d0i3 streaming is enabled if all the active streams can
410+
* work in d0i3 state and playback is enabled
411+
*/
412+
static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev)
413+
{
414+
struct snd_pcm_substream *substream;
415+
struct snd_sof_pcm *spcm;
416+
bool playback_active = false;
417+
int dir;
418+
419+
list_for_each_entry(spcm, &sdev->pcm_list, list) {
420+
for_each_pcm_streams(dir) {
421+
substream = spcm->stream[dir].substream;
422+
if (!substream || !substream->runtime)
423+
continue;
424+
425+
if (!spcm->stream[dir].d0i3_compatible)
426+
return false;
427+
428+
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
429+
playback_active = true;
430+
}
431+
}
432+
433+
return playback_active;
434+
}
435+
415436
static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
416437
const struct sof_dsp_power_state *target_state)
417438
{
@@ -453,6 +474,9 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
453474
!hda_enable_trace_D0I3_S0 ||
454475
sdev->system_suspend_target != SOF_SUSPEND_NONE)
455476
flags = HDA_PM_NO_DMA_TRACE;
477+
478+
if (hda_dsp_d0i3_streaming_applicable(sdev))
479+
flags |= HDA_PM_PG_STREAMING;
456480
} else {
457481
/* prevent power gating in D0I0 */
458482
flags = HDA_PM_PPG;

sound/soc/sof/intel/hda-ipc.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,32 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
6767
return 0;
6868
}
6969

70+
static inline bool hda_dsp_ipc4_pm_msg(u32 primary)
71+
{
72+
/* pm setting is only supported by module msg */
73+
if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)
74+
return false;
75+
76+
if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||
77+
SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)
78+
return true;
79+
80+
return false;
81+
}
82+
83+
void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
84+
struct snd_sof_ipc_msg *msg)
85+
{
86+
struct sof_ipc4_msg *msg_data = msg->msg_data;
87+
88+
/* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */
89+
if (hda_dsp_ipc4_pm_msg(msg_data->primary))
90+
return;
91+
92+
mod_delayed_work(system_wq, &hdev->d0i3_work,
93+
msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
94+
}
95+
7096
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
7197
{
7298
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)
88114
snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
89115
msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
90116

117+
hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
118+
91119
return 0;
92120
}
93121

sound/soc/sof/intel/hda.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,8 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
919919
int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
920920
irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
921921
bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev);
922+
void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
923+
struct snd_sof_ipc_msg *msg);
922924
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
923925
void hda_ipc4_dump(struct snd_sof_dev *sdev);
924926
extern struct sdw_intel_ops sdw_callback;

sound/soc/sof/intel/mtl.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms
110110
snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR,
111111
msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY);
112112

113+
hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
114+
113115
return 0;
114116
}
115117

sound/soc/sof/ipc3.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,10 +1077,28 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
10771077
return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
10781078
}
10791079

1080+
static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
1081+
{
1082+
struct sof_ipc_pm_gate pm_gate;
1083+
struct sof_ipc_reply reply;
1084+
1085+
memset(&pm_gate, 0, sizeof(pm_gate));
1086+
1087+
/* configure pm_gate ipc message */
1088+
pm_gate.hdr.size = sizeof(pm_gate);
1089+
pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
1090+
pm_gate.flags = flags;
1091+
1092+
/* send pm_gate ipc to dsp */
1093+
return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
1094+
&reply, sizeof(reply));
1095+
}
1096+
10801097
static const struct sof_ipc_pm_ops ipc3_pm_ops = {
10811098
.ctx_save = sof_ipc3_ctx_save,
10821099
.ctx_restore = sof_ipc3_ctx_restore,
10831100
.set_core_state = sof_ipc3_set_core_state,
1101+
.set_pm_gate = sof_ipc3_set_pm_gate,
10841102
};
10851103

10861104
const struct sof_ipc_ops ipc3_ops = {

sound/soc/sof/ipc4.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,17 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
370370
if (!msg_data)
371371
return -EINVAL;
372372

373+
if (!no_pm) {
374+
const struct sof_dsp_power_state target_state = {
375+
.state = SOF_DSP_PM_D0,
376+
};
377+
378+
/* ensure the DSP is in D0i0 before sending a new IPC */
379+
ret = snd_sof_dsp_set_power_state(sdev, &target_state);
380+
if (ret < 0)
381+
return ret;
382+
}
383+
373384
/* Serialise IPC TX */
374385
mutex_lock(&ipc->tx_mutex);
375386

@@ -656,9 +667,22 @@ static int sof_ipc4_ctx_save(struct snd_sof_dev *sdev)
656667
return sof_ipc4_set_core_state(sdev, SOF_DSP_PRIMARY_CORE, false);
657668
}
658669

670+
static int sof_ipc4_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
671+
{
672+
struct sof_ipc4_msg msg = {{0}};
673+
674+
msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_SET_D0IX);
675+
msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
676+
msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
677+
msg.extension = flags;
678+
679+
return sof_ipc4_tx_msg(sdev, &msg, 0, NULL, 0, true);
680+
}
681+
659682
static const struct sof_ipc_pm_ops ipc4_pm_ops = {
660683
.ctx_save = sof_ipc4_ctx_save,
661684
.set_core_state = sof_ipc4_set_core_state,
685+
.set_pm_gate = sof_ipc4_set_pm_gate,
662686
};
663687

664688
static int sof_ipc4_init(struct snd_sof_dev *sdev)

sound/soc/sof/sof-priv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,13 @@ struct sof_ipc_fw_tracing_ops {
425425
* @ctx_save: Optional function pointer for context save
426426
* @ctx_restore: Optional function pointer for context restore
427427
* @set_core_state: Optional function pointer for turning on/off a DSP core
428+
* @set_pm_gate: Optional function pointer for pm gate settings
428429
*/
429430
struct sof_ipc_pm_ops {
430431
int (*ctx_save)(struct snd_sof_dev *sdev);
431432
int (*ctx_restore)(struct snd_sof_dev *sdev);
432433
int (*set_core_state)(struct snd_sof_dev *sdev, int core_idx, bool on);
434+
int (*set_pm_gate)(struct snd_sof_dev *sdev, u32 flags);
433435
};
434436

435437
/**

0 commit comments

Comments
 (0)