Skip to content
25 changes: 25 additions & 0 deletions drivers/soundwire/intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,24 @@ static int intel_params_stream(struct sdw_intel *sdw,
* DAI routines
*/

static int intel_free_stream(struct sdw_intel *sdw,
Comment thread
ranj063 marked this conversation as resolved.
struct snd_pcm_substream *substream,
struct snd_soc_dai *dai,
int link_id)
{
struct sdw_intel_link_res *res = sdw->link_res;
struct sdw_intel_stream_free_data free_data;

free_data.substream = substream;
free_data.dai = dai;
free_data.link_id = link_id;

if (res->ops && res->ops->free_stream && res->dev)
return res->ops->free_stream(res->dev, &free_data);

return 0;
}

static int intel_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
Expand Down Expand Up @@ -831,6 +849,7 @@ static int
intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
struct sdw_intel *sdw = cdns_to_intel(cdns);
struct sdw_cdns_dai_runtime *dai_runtime;
int ret;

Expand All @@ -851,6 +870,12 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
return ret;
}

ret = intel_free_stream(sdw, substream, dai, sdw->instance);
if (ret < 0) {
dev_err(dai->dev, "intel_free_stream: failed %d\n", ret);
return ret;
}
Comment thread
ujfalusi marked this conversation as resolved.

dai_runtime->pdi = NULL;

return 0;
Expand Down
8 changes: 0 additions & 8 deletions sound/soc/sof/intel/hda-dai.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ static bool hda_use_tplg_nhlt;
module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");

static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;

return snd_soc_component_get_drvdata(component);
}

int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
struct snd_sof_dai_config_data *data)
{
Expand Down
25 changes: 25 additions & 0 deletions sound/soc/sof/intel/hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "../sof-audio.h"
#include "../sof-pci-dev.h"
#include "../ops.h"
#include "../ipc4-topology.h"
#include "hda.h"

#include <trace/events/sof_intel.h>
Expand Down Expand Up @@ -68,8 +69,32 @@ static int sdw_params_stream(struct device *dev,
return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
}

static int sdw_params_free(struct device *dev, struct sdw_intel_stream_free_data *free_data)
{
struct snd_soc_dai *d = free_data->dai;
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, free_data->substream->stream);
struct snd_sof_dev *sdev = widget_to_sdev(w);

if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier_data *copier_data;
struct sof_ipc4_copier *ipc4_copier;

ipc4_copier = dai->private;
ipc4_copier->dai_index = 0;
copier_data = &ipc4_copier->data;

/* clear the node ID */
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
}

return 0;
}

struct sdw_intel_ops sdw_callback = {
.params_stream = sdw_params_stream,
.free_stream = sdw_params_free,
};

static int sdw_ace2x_params_stream(struct device *dev,
Expand Down
8 changes: 8 additions & 0 deletions sound/soc/sof/intel/hda.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,4 +1025,12 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream,
struct snd_soc_dai *cpu_dai);

static inline struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;

return snd_soc_component_get_drvdata(component);
Comment thread
ranj063 marked this conversation as resolved.
}

#endif
16 changes: 10 additions & 6 deletions sound/soc/sof/ipc4-topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
}

if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
struct sof_ipc4_alh_configuration_blob *blob;
unsigned int group_id;

Expand All @@ -1287,9 +1286,6 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
ALH_MULTI_GTW_BASE;
ida_free(&alh_group_ida, group_id);
}

/* clear the node ID */
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
}
}

Expand Down Expand Up @@ -1717,14 +1713,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
*/
i = 0;
list_for_each_entry(w, &sdev->widget_list, list) {
u32 node_type;

if (w->widget->sname &&
strcmp(w->widget->sname, swidget->widget->sname))
continue;

dai = w->private;
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id;
node_type = SOF_IPC4_GET_NODE_TYPE(alh_data->gtw_cfg.node_id);
blob->alh_cfg.mapping[i].device = SOF_IPC4_NODE_TYPE(node_type);
blob->alh_cfg.mapping[i].device |=
SOF_IPC4_NODE_INDEX(alh_copier->dai_index);

/*
* The mapping[i] device in ALH blob should be the same as the
Expand Down Expand Up @@ -2843,12 +2844,15 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
/*
* Do not clear the node ID when this op is invoked with
* SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during
* unprepare.
* unprepare. The node_id for multi-gateway DAI's will be overwritten with the
* group_id during copier's ipc_prepare op.
*/
if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
ipc4_copier->dai_index = data->dai_node_id;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_node_id);
}

break;
case SOF_DAI_INTEL_DMIC:
case SOF_DAI_INTEL_SSP:
Expand Down
1 change: 1 addition & 0 deletions sound/soc/sof/ipc4-topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#define SOF_IPC4_NODE_INDEX_MASK 0xFF
#define SOF_IPC4_NODE_INDEX(x) ((x) & SOF_IPC4_NODE_INDEX_MASK)
#define SOF_IPC4_NODE_TYPE(x) ((x) << 8)
#define SOF_IPC4_GET_NODE_TYPE(node_id) ((node_id) >> 8)

/* Node ID for SSP type DAI copiers */
#define SOF_IPC4_NODE_INDEX_INTEL_SSP(x) (((x) & 0xf) << 4)
Expand Down
58 changes: 27 additions & 31 deletions sound/soc/sof/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,8 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
int ret, err = 0;
int ret;

/* nothing to do for BE */
if (rtd->dai_link->no_pcm)
Expand All @@ -211,42 +210,18 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);

if (spcm->prepared[substream->stream]) {
/* stop DMA first if needed */
if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);

/* free PCM in the DSP */
if (pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
err = ret;
}

spcm->prepared[substream->stream] = false;
}

/* reset DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0) {
dev_err(component->dev, "error: platform hw free failed\n");
err = ret;
}

/* free the DAPM widget list */
ret = sof_widget_list_free(sdev, spcm, substream->stream);
if (ret < 0)
err = ret;
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
Comment thread
ujfalusi marked this conversation as resolved.

cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);

return err;
return ret;
}

static int sof_pcm_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
int ret;

Expand All @@ -258,8 +233,18 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;

if (spcm->prepared[substream->stream])
return 0;
if (spcm->prepared[substream->stream]) {
if (!spcm->pending_stop[substream->stream])
return 0;

/*
* this case should be reached in case of xruns where we absolutely
* want to free-up and reset all PCM/DMA resources
*/
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
if (ret < 0)
return ret;
}

dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
Expand Down Expand Up @@ -302,6 +287,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
spcm->pcm.pcm_id, substream->stream, cmd);

spcm->pending_stop[substream->stream] = false;

switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ipc_first = true;
Expand Down Expand Up @@ -370,6 +357,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);

/*
* set the pending_stop flag to indicate that pipeline stop has been delayed.
* This will be used later to stop the pipelines during prepare when recovering
* from xruns.
*/
if (pcm_ops && pcm_ops->platform_stop_during_hw_free &&
Comment thread
ranj063 marked this conversation as resolved.
cmd == SNDRV_PCM_TRIGGER_STOP)
spcm->pending_stop[substream->stream] = true;
break;
default:
break;
Expand Down
29 changes: 21 additions & 8 deletions sound/soc/sof/sof-audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -834,35 +834,48 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
{
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
int err = 0;

if (spcm->prepared[substream->stream]) {
/* stop DMA first if needed */
if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);

/* Send PCM_FREE IPC to reset pipeline */
/* free PCM in the DSP */
if (pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
if (ret < 0) {
dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n",
__func__, ret);
err = ret;
}
}

spcm->prepared[substream->stream] = false;
spcm->pending_stop[substream->stream] = false;
}

/* reset the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0)
return ret;
if (ret < 0) {
dev_err(sdev->dev, "%s: platform hw free failed %d\n",
__func__, ret);
if (!err)
err = ret;
}

/* free widget list */
if (free_widget_list) {
ret = sof_widget_list_free(sdev, spcm, dir);
if (ret < 0)
dev_err(sdev->dev, "failed to free widgets during suspend\n");
if (ret < 0) {
dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n",
__func__, ret);
if (!err)
err = ret;
}
}

return ret;
return err;
}

/*
Expand Down
1 change: 1 addition & 0 deletions sound/soc/sof/sof-audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ struct snd_sof_pcm {
struct list_head list; /* list in sdev pcm list */
struct snd_pcm_hw_params params[2];
bool prepared[2]; /* PCM_PARAMS set successfully */
bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */
};

struct snd_sof_led_control {
Expand Down