Skip to content
Open
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;

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);
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;
Comment on lines +686 to +688

/* 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);
Comment thread
bardliao marked this conversation as resolved.
Resolved
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
1 change: 1 addition & 0 deletions sound/soc/soc-dapm.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card)

snd_soc_dapm_mutex_unlock(card);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mark_endpoints_dirty);

/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
Expand Down
1 change: 1 addition & 0 deletions sound/soc/sof/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->dai_list);
INIT_LIST_HEAD(&sdev->dai_link_list);
INIT_LIST_HEAD(&sdev->route_list);
INIT_LIST_HEAD(&sdev->ignore_suspend_widget_list);
INIT_LIST_HEAD(&sdev->ipc_client_list);
INIT_LIST_HEAD(&sdev->ipc_rx_handler_list);
INIT_LIST_HEAD(&sdev->fw_state_handler_list);
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