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
1 change: 1 addition & 0 deletions drivers/soundwire/intel.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct sdw_intel {
struct sdw_intel_link_res *link_res;
bool startup_done;
struct sdw_intel_bpt bpt_ctx;
bool suspend_bus_retained;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
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
63 changes: 61 additions & 2 deletions 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 @@ -576,13 +577,23 @@ int intel_resume_child_device(struct device *dev, void *data)
return 0;
}

static bool intel_ignore_suspend(struct sdw_intel *sdw)
{
const struct sdw_intel_link_res *res = sdw->link_res;

if (res->ops && res->ops->ignore_suspend)
return res->ops->ignore_suspend(res->dev);

return pm_suspend_target_state == PM_SUSPEND_TO_IDLE;
}

static int __maybe_unused intel_pm_prepare(struct device *dev)
{
struct sdw_cdns *cdns = dev_get_drvdata(dev);
struct sdw_intel *sdw = cdns_to_intel(cdns);
struct sdw_bus *bus = &cdns->bus;
u32 clock_stop_quirks;
int ret;
int i, ret, stream_error = 0;

if (bus->prop.hw_disabled || !sdw->startup_done) {
dev_dbg(dev, "SoundWire master %d is disabled or not-started, ignoring\n",
Expand All @@ -591,6 +602,7 @@ static int __maybe_unused intel_pm_prepare(struct device *dev)
}

clock_stop_quirks = sdw->link_res->clock_stop_quirks;
sdw->suspend_bus_retained = false;

if (pm_runtime_suspended(dev) &&
pm_runtime_suspended(dev->parent) &&
Expand Down Expand Up @@ -633,7 +645,38 @@ static int __maybe_unused intel_pm_prepare(struct device *dev)
dev_err(dev, "%s: intel_resume_child_device failed: %d\n", __func__, ret);
}

return 0;
/*
* Revoke retention from an earlier S0ix before any controller or codec
* suspends. An already-suspended PCM will not issue another trigger.
*/
if (!intel_ignore_suspend(sdw)) {
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;

stream->ignore_suspend = false;
dai_runtime->suspended = true;

if (stream->state == SDW_STREAM_ENABLED) {
ret = sdw_disable_stream(stream);
Comment thread
Copilot marked this conversation as resolved.
Resolved
if (ret) {
dev_err(dev, "failed to disable retained stream %s: %d; close and reopen PCM\n",
stream->name, ret);
if (!stream_error)
stream_error = ret;
}
}
}
}

return stream_error;
}

static int __maybe_unused intel_suspend(struct device *dev)
Expand All @@ -650,6 +693,15 @@ 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 (intel_ignore_suspend(sdw) && sdw_bus_ignore_suspend(bus)) {
sdw->suspend_bus_retained = true;
return 0;
}

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

Expand Down Expand Up @@ -679,6 +731,7 @@ static int __maybe_unused intel_suspend(struct device *dev)
ret = sdw_intel_stop_bus(sdw, true, false);
if (ret < 0) {
dev_err(dev, "%s: cannot stop bus: %d\n", __func__, ret);
pm_runtime_enable(dev);
return ret;
}

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

if (sdw->suspend_bus_retained) {
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
1 change: 1 addition & 0 deletions include/linux/soundwire/sdw_intel.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ struct sdw_intel_ops {
int (*free_stream)(struct device *dev,
struct sdw_intel_stream_free_data *free_data);
int (*trigger)(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai);
bool (*ignore_suspend)(struct device *dev);
};

/**
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