Commit d62d6210 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: Intel: improve SoundWire support for

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

This patchset contains important updates for SoundWire support.

We initially implemented support for multiple amplifiers on different
links using a single HDaudio DMA transfer. To align with the other OS,
the 'aggregation' is now supported by the firmware. This change in
directions has kernel impacts, since we now have multiple HDaudio DMAs
to program and start, but since there are no platforms released so far
there's no end-user impact.

In addition, the behavior in case of xruns is improved by clearing the
PCM states and better handling of the hw_free case.

Note that the hw_free support will compile but will only be functional
with the companion patch "soundwire: intel: add intel_free_stream() back"
already applied in the SoundWire tree.
parents dbc93a55 f0caa4fc
......@@ -145,17 +145,9 @@ static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *dai;
struct hdac_ext_stream *hext_stream;
/* only allocate a stream_tag for the first DAI in the dailink */
dai = snd_soc_rtd_to_cpu(rtd, 0);
if (dai == cpu_dai)
hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
else
hext_stream = snd_soc_dai_get_dma_data(dai, substream);
hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
if (!hext_stream)
return NULL;
......@@ -168,14 +160,9 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai
struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *dai;
/* only release a stream_tag for the first DAI in the dailink */
dai = snd_soc_rtd_to_cpu(rtd, 0);
if (dai == cpu_dai)
snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
}
static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
......@@ -435,28 +422,6 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c
return ret;
}
static struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier = dai->private;
struct sof_ipc4_alh_configuration_blob *blob;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
/*
* Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using
* the multi-gateway firmware configuration. The DMA hardware can take care of
* multiple links without needing any firmware assistance
*/
blob->alh_cfg.device_count = 1;
return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream);
}
static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
.get_hext_stream = hda_ipc4_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
......@@ -498,7 +463,7 @@ static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
};
static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
.get_hext_stream = sdw_hda_ipc4_get_hext_stream,
.get_hext_stream = hda_ipc4_get_hext_stream,
.assign_hext_stream = hda_assign_hext_stream,
.release_hext_stream = hda_release_hext_stream,
.setup_hext_stream = hda_setup_hext_stream,
......
......@@ -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)
{
......@@ -221,15 +213,15 @@ static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
}
static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai,
struct snd_sof_dai_config_data *data,
unsigned int flags)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
struct hdac_ext_stream *hext_stream;
struct snd_sof_dai_config_data data = { 0 };
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
struct snd_sof_dev *sdev = widget_to_sdev(w);
int ret;
......@@ -249,9 +241,19 @@ static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
hext_stream = ops->get_hext_stream(sdev, dai, substream);
flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
data->dai_data = hdac_stream(hext_stream)->stream_tag - 1;
return hda_dai_config(w, flags, &data);
return hda_dai_config(w, flags, data);
}
static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_sof_dai_config_data data = { 0 };
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
return hda_dai_hw_params_data(substream, params, dai, &data, flags);
}
/*
......@@ -341,11 +343,14 @@ static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
return ipc4_copier;
}
static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai,
struct snd_sof_dai_config_data *data,
unsigned int flags)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sof_ipc4_dma_config_tlv *dma_config_tlv;
const struct hda_dai_widget_dma_ops *ops;
struct sof_ipc4_dma_config *dma_config;
......@@ -353,6 +358,8 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct snd_sof_dev *sdev;
struct snd_soc_dai *dai;
int cpu_dai_id;
int stream_id;
int ret;
......@@ -363,9 +370,9 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
}
/* use HDaudio stream handling */
ret = hda_dai_hw_params(substream, params, cpu_dai);
ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
if (ret < 0) {
dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret);
dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret);
return ret;
}
......@@ -392,7 +399,12 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
/* configure TLV */
ipc4_copier = widget_to_copier(w);
dma_config_tlv = &ipc4_copier->dma_config_tlv;
for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
if (dai == cpu_dai)
break;
}
dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
/* dma_config_priv_size is zero */
dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
......@@ -403,13 +415,27 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
dma_config->pre_allocated_by_host = 1;
dma_config->dma_channel_id = stream_id - 1;
dma_config->stream_id = stream_id;
dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
/*
* Currently we use a DMA for each device in ALH blob. The device will
* be copied in sof_ipc4_prepare_copier_module.
*/
dma_config->dma_stream_channel_map.device_count = 1;
dma_config->dma_priv_config_size = 0;
skip_tlv:
return 0;
}
static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
struct snd_sof_dai_config_data data = { 0 };
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
}
static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
......@@ -436,15 +462,29 @@ static const struct snd_soc_dai_ops dmic_dai_ops = {
int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai,
int link_id)
int link_id,
int intel_alh_id)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sof_ipc4_dma_config_tlv *dma_config_tlv;
struct snd_sof_dai_config_data data = { 0 };
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
const struct hda_dai_widget_dma_ops *ops;
struct sof_ipc4_dma_config *dma_config;
struct sof_ipc4_copier *ipc4_copier;
struct hdac_ext_stream *hext_stream;
struct snd_soc_dai *dai;
struct snd_sof_dev *sdev;
bool cpu_dai_found = false;
int cpu_dai_id;
int ch_mask;
int ret;
int i;
ret = non_hda_dai_hw_params(substream, params, cpu_dai);
data.dai_index = (link_id << 8) | cpu_dai->id;
data.dai_node_id = intel_alh_id;
ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
if (ret < 0) {
dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
return ret;
......@@ -457,9 +497,25 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
if (!hext_stream)
return -ENODEV;
/* in the case of SoundWire we need to program the PCMSyCM registers */
/*
* in the case of SoundWire we need to program the PCMSyCM registers. In case
* of aggregated devices, we need to define the channel mask for each sublink
* by reconstructing the split done in soc-pcm.c
*/
for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
if (dai == cpu_dai) {
cpu_dai_found = true;
break;
}
}
if (!cpu_dai_found)
return -ENODEV;
ch_mask = GENMASK(params_channels(params) - 1, 0);
ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
GENMASK(params_channels(params) - 1, 0),
ch_mask,
hdac_stream(hext_stream)->stream_tag,
substream->stream);
if (ret < 0) {
......@@ -468,6 +524,22 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
ipc4_copier = widget_to_copier(w);
dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
dma_config = &dma_config_tlv->dma_config;
dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index;
dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask;
/*
* copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier
* will be handled in sof_ipc4_prepare_copier_module.
*/
for_each_rtd_cpu_dais(rtd, i, dai) {
w = snd_soc_dai_get_widget(dai, substream->stream);
ipc4_copier = widget_to_copier(w);
memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
sizeof(*dma_config_tlv));
}
return 0;
}
......
......@@ -31,6 +31,7 @@
#include "../sof-audio.h"
#include "../sof-pci-dev.h"
#include "../ops.h"
#include "../ipc4-topology.h"
#include "hda.h"
#include "telemetry.h"
......@@ -145,12 +146,37 @@ static int sdw_params_stream(struct device *dev,
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;
data.dai_node_id = data.dai_data;
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,
......@@ -159,7 +185,8 @@ static int sdw_ace2x_params_stream(struct device *dev,
return sdw_hda_dai_hw_params(params_data->substream,
params_data->hw_params,
params_data->dai,
params_data->link_id);
params_data->link_id,
params_data->alh_stream_id);
}
static int sdw_ace2x_free_stream(struct device *dev,
......
......@@ -844,7 +844,8 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)
int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai,
int link_id);
int link_id,
int intel_alh_id);
int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai,
......@@ -999,4 +1000,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);
}
#endif
......@@ -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;
......@@ -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;
}
}
......@@ -1454,6 +1450,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 deep_buffer_dma_ms = 0;
int output_fmt_index;
bool single_output_format;
int i;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
......@@ -1671,6 +1668,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
*/
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
struct sof_ipc4_dma_config *dma_config;
struct sof_ipc4_copier_data *alh_data;
struct sof_ipc4_copier *alh_copier;
struct snd_sof_widget *w;
......@@ -1679,7 +1677,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
u32 ch_map;
u32 step;
u32 mask;
int i;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
......@@ -1703,6 +1700,8 @@ 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;
......@@ -1710,7 +1709,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
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
* dma_config_tlv[i] mapping device if a dma_config_tlv is present.
* The device id will be used for DMA tlv mapping purposes.
*/
if (ipc4_copier->dma_config_tlv[i].length) {
dma_config = &ipc4_copier->dma_config_tlv[i].dma_config;
blob->alh_cfg.mapping[i].device =
dma_config->dma_stream_channel_map.mapping[0].device;
}
/*
* Set the same channel mask for playback as the audio data is
* duplicated for all speakers. For capture, split the channels
......@@ -1789,19 +1803,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;
ipc_size = sizeof(*copier_data) + gtw_cfg_config_length;
if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID &&
ipc4_copier->dma_config_tlv.length) {
dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) +
ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size;
/* paranoia check on TLV size/length */
if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length +
sizeof(uint32_t) * 2) {
dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n",
dma_config_tlv_size, ipc4_copier->dma_config_tlv.length);
return -EINVAL;
}
dma_config_tlv_size = 0;
for (i = 0; i < SOF_IPC4_DMA_DEVICE_MAX_COUNT; i++) {
if (ipc4_copier->dma_config_tlv[i].type != SOF_IPC4_GTW_DMA_CONFIG_ID)
continue;
dma_config_tlv_size += ipc4_copier->dma_config_tlv[i].length;
dma_config_tlv_size +=
ipc4_copier->dma_config_tlv[i].dma_config.dma_priv_config_size;
dma_config_tlv_size += (sizeof(ipc4_copier->dma_config_tlv[i]) -
sizeof(ipc4_copier->dma_config_tlv[i].dma_config));
}
if (dma_config_tlv_size) {
ipc_size += dma_config_tlv_size;
/* we also need to increase the size at the gtw level */
......@@ -2813,17 +2826,24 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
case SOF_DAI_INTEL_HDA:
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
fallthrough;
if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
}
break;
case SOF_DAI_INTEL_ALH:
/*
* 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_data);
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:
......
......@@ -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)
......@@ -313,7 +314,7 @@ struct sof_ipc4_copier {
struct sof_ipc4_gtw_attributes *gtw_attr;
u32 dai_type;
int dai_index;
struct sof_ipc4_dma_config_tlv dma_config_tlv;
struct sof_ipc4_dma_config_tlv dma_config_tlv[SOF_IPC4_DMA_DEVICE_MAX_COUNT];
};
/**
......
......@@ -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)
......@@ -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);
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;
......@@ -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);
......@@ -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;
......@@ -371,6 +358,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 &&
cmd == SNDRV_PCM_TRIGGER_STOP)
spcm->pending_stop[substream->stream] = true;
break;
default:
break;
......
......@@ -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;
}
/*
......
......@@ -91,6 +91,7 @@ struct snd_sof_pcm;
struct snd_sof_dai_config_data {
int dai_index;
int dai_data; /* contains DAI-specific information */
int dai_node_id; /* contains DAI-specific information for Gateway configuration */
};
/**
......@@ -341,6 +342,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 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment