Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions drivers/soundwire/intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/io.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <sound/soc.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
Expand Down Expand Up @@ -952,11 +953,19 @@ static int intel_component_dais_suspend(struct snd_soc_component *component)
for_each_component_dais(component, dai) {
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
struct sdw_cdns_dai_runtime *dai_runtime;
struct sdw_stream_runtime *stream;

dai_runtime = cdns->dai_runtime_array[dai->id];
if (!dai_runtime)
continue;

if (dai_runtime)
dai_runtime->suspended = true;
stream = dai_runtime->stream;
if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE &&
stream && stream->ignore_suspend &&
stream->state == SDW_STREAM_ENABLED)
continue;
Comment thread
bardliao marked this conversation as resolved.
Resolved

dai_runtime->suspended = true;
}

return 0;
Expand Down
13 changes: 11 additions & 2 deletions drivers/soundwire/intel_ace2x.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/suspend.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h>
Expand Down Expand Up @@ -951,11 +952,19 @@ static int intel_component_dais_suspend(struct snd_soc_component *component)
for_each_component_dais(component, dai) {
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
struct sdw_cdns_dai_runtime *dai_runtime;
struct sdw_stream_runtime *stream;

dai_runtime = cdns->dai_runtime_array[dai->id];
if (!dai_runtime)
continue;

if (dai_runtime)
dai_runtime->suspended = true;
stream = dai_runtime->stream;
if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE &&
stream && stream->ignore_suspend &&
stream->state == SDW_STREAM_ENABLED)
continue;

dai_runtime->suspended = true;
}

return 0;
Expand Down
46 changes: 45 additions & 1 deletion drivers/soundwire/intel_auxdevice.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/auxiliary_bus.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <sound/soc.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw.h>
Expand Down Expand Up @@ -582,7 +583,7 @@ static int __maybe_unused intel_pm_prepare(struct device *dev)
struct sdw_intel *sdw = cdns_to_intel(cdns);
struct sdw_bus *bus = &cdns->bus;
u32 clock_stop_quirks;
int ret;
int i, ret;

if (bus->prop.hw_disabled || !sdw->startup_done) {
dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
Expand Down Expand Up @@ -633,6 +634,34 @@ static int __maybe_unused intel_pm_prepare(struct device *dev)
dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret);
}

/*
* Revoke retention from an earlier S0ix before any controller or codec
* suspends. An already-suspended PCM will not issue another trigger.
*/
if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE) {
for (i = 0; i < cdns->pcm.num_pdi; i++) {
struct sdw_cdns_dai_runtime *dai_runtime = cdns->dai_runtime_array[i];
struct sdw_stream_runtime *stream;

if (!dai_runtime || !dai_runtime->stream)
continue;

stream = dai_runtime->stream;
if (!stream->ignore_suspend)
continue;

if (stream->state == SDW_STREAM_ENABLED) {
ret = sdw_disable_stream(stream);
Comment on lines +653 to +654
if (ret) {
dev_err(dev, "failed to disable retained stream %s: %d\n",
stream->name, ret);
return ret;
}
}
stream->ignore_suspend = false;
}
}

return 0;
}

Expand All @@ -650,6 +679,14 @@ static int __maybe_unused intel_suspend(struct device *dev)
return 0;
}

/*
* ignore_suspend is set by the PCM trigger for S0ix and may remain set
* if a later sleep transition does not issue another PCM trigger.
*/
if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE &&
sdw_bus_ignore_suspend(bus))
return 0;

/* Prevent runtime PM from racing with the code below. */
pm_runtime_disable(dev);

Expand Down Expand Up @@ -737,6 +774,13 @@ static int __maybe_unused intel_resume(struct device *dev)
return 0;
}

if (pm_suspend_target_state == PM_SUSPEND_TO_IDLE &&
sdw_bus_ignore_suspend(bus)) {
dev_dbg(dev, "SoundWire master %d remained active during system suspend\n",
bus->link_id);
return 0;
}

ret = sdw_intel_link_power_up(sdw);
if (ret) {
dev_err(dev, "%s failed: %d\n", __func__, ret);
Expand Down
19 changes: 19 additions & 0 deletions drivers/soundwire/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,25 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream)
}
EXPORT_SYMBOL(sdw_enable_stream);

bool sdw_bus_ignore_suspend(struct sdw_bus *bus)
{
struct sdw_master_runtime *m_rt;
bool ret = false;

mutex_lock(&bus->bus_lock);
list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
if (m_rt->stream->state == SDW_STREAM_ENABLED &&
m_rt->stream->ignore_suspend) {
ret = true;
break;
}
}
mutex_unlock(&bus->bus_lock);

return ret;
}
EXPORT_SYMBOL(sdw_bus_ignore_suspend);

static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
{
struct sdw_master_runtime *m_rt;
Expand Down
3 changes: 3 additions & 0 deletions include/linux/soundwire/sdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@ struct sdw_stream_params {
* @master_list: List of Master runtime(s) in this stream.
* master_list can contain only one m_rt per Master instance
* for a stream
* @ignore_suspend: stream keeps running during system suspend
*/
struct sdw_stream_runtime {
const char *name;
Expand All @@ -986,6 +987,7 @@ struct sdw_stream_runtime {
enum sdw_stream_type type;
int m_rt_count;
struct list_head master_list;
bool ignore_suspend;
};

/**
Expand Down Expand Up @@ -1086,6 +1088,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus,
int sdw_startup_stream(void *sdw_substream);
int sdw_prepare_stream(struct sdw_stream_runtime *stream);
int sdw_enable_stream(struct sdw_stream_runtime *stream);
bool sdw_bus_ignore_suspend(struct sdw_bus *bus);
int sdw_disable_stream(struct sdw_stream_runtime *stream);
int sdw_deprepare_stream(struct sdw_stream_runtime *stream);
void sdw_shutdown_stream(void *sdw_substream);
Expand Down
21 changes: 20 additions & 1 deletion sound/soc/sdw_utils/soc_sdw_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1880,6 +1880,9 @@ int asoc_sdw_prepare(struct snd_pcm_substream *substream)
return PTR_ERR(sdw_stream);
}

if (sdw_stream->state == SDW_STREAM_ENABLED)
return 0;

return sdw_prepare_stream(sdw_stream);
}
EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS");
Expand All @@ -1902,6 +1905,10 @@ int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)

switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
if (sdw_stream->state == SDW_STREAM_ENABLED) {
ret = 0;
break;
}
/*
* The peripherals lose their port configuration when the
* controller is power-gated during system suspend, and an
Expand All @@ -1923,10 +1930,12 @@ int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd)
break;

case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
ret = sdw_disable_stream(sdw_stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ret = sdw_stream->ignore_suspend ? 0 : sdw_disable_stream(sdw_stream);
break;
default:
ret = -EINVAL;
break;
Expand Down Expand Up @@ -2005,6 +2014,16 @@ int asoc_sdw_hw_free(struct snd_pcm_substream *substream)
return PTR_ERR(sdw_stream);
}

if (sdw_stream->state == SDW_STREAM_ENABLED) {
int ret;

ret = sdw_disable_stream(sdw_stream);
if (ret) {
dev_err(rtd->dev, "failed to disable SoundWire stream: %d\n", ret);
return ret;
}
}

return sdw_deprepare_stream(sdw_stream);
}
EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS");
Expand Down
49 changes: 47 additions & 2 deletions sound/soc/soc-dapm.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/initval.h>
#include "soc-internal.h"

Expand Down Expand Up @@ -1415,18 +1416,62 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w)
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
*/
static bool dapm_be_has_active_ignore_suspend_fe(struct snd_soc_dapm_widget *widget)
{
struct snd_soc_pcm_runtime *be;
struct snd_soc_dpcm *dpcm;
struct snd_soc_dai *be_dai, *fe_dai;
struct snd_soc_dapm_widget *fe_widget;
int i, j, stream;

if (widget->id == snd_soc_dapm_dai_in)
stream = SNDRV_PCM_STREAM_PLAYBACK;
else if (widget->id == snd_soc_dapm_dai_out)
stream = SNDRV_PCM_STREAM_CAPTURE;
else
return false;

/* DPCM client links are stable while the card is in system suspend. */
for_each_card_rtds(widget->dapm->card, be) {
if (!be->dai_link->no_pcm)
continue;

for_each_rtd_dais(be, i, be_dai) {
if (snd_soc_dai_get_widget(be_dai, stream) != widget)
continue;

for_each_dpcm_fe(be, stream, dpcm) {
if (dpcm->state == SND_SOC_DPCM_LINK_STATE_FREE)
continue;

for_each_rtd_cpu_dais(dpcm->fe, j, fe_dai) {
fe_widget = snd_soc_dai_get_widget(fe_dai, stream);
if (fe_widget && fe_widget->active &&
fe_widget->ignore_suspend)
return true;
}
}
}
}

return false;
}

static int dapm_suspend_check(struct snd_soc_dapm_widget *widget)
{
struct device *dev = snd_soc_dapm_to_dev(widget->dapm);
int level = snd_power_get_state(widget->dapm->card->snd_card);
bool ignore_suspend;

switch (level) {
case SNDRV_CTL_POWER_D3hot:
case SNDRV_CTL_POWER_D3cold:
if (widget->ignore_suspend)
ignore_suspend = widget->ignore_suspend ||
dapm_be_has_active_ignore_suspend_fe(widget);
if (ignore_suspend)
dev_dbg(dev, "ASoC: %s ignoring suspend\n",
widget->name);
return widget->ignore_suspend;
return ignore_suspend;
default:
return 1;
}
Expand Down
4 changes: 4 additions & 0 deletions sound/soc/sof/intel/hda-dai-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,10 @@ static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);

if (sdev->fw_state != SOF_FW_BOOT_COMPLETE &&
(cmd == SNDRV_PCM_TRIGGER_STOP || cmd == SNDRV_PCM_TRIGGER_SUSPEND))
return 0;

switch (cmd) {
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
Expand Down
Loading
Loading