summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2023-02-16 14:32:04 +0100
committerTakashi Iwai <tiwai@suse.de>2023-02-16 14:32:04 +0100
commit1bdb78368f9448cf0de9b11eb1388e887d1b4849 (patch)
tree82a70b8d8502d5078dc36d67fbb7bc315e760d63 /sound/soc/sof
parent5661706efa200252d0e9fea02421b0a5857808c3 (diff)
parent76f5aaabce492aa6991c28c96bb78b00b05d06c5 (diff)
downloadlinux-stable-1bdb78368f9448cf0de9b11eb1388e887d1b4849.tar.gz
linux-stable-1bdb78368f9448cf0de9b11eb1388e887d1b4849.tar.bz2
linux-stable-1bdb78368f9448cf0de9b11eb1388e887d1b4849.zip
Merge tag 'asoc-v6.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v6.3 There's been quite a lot of activity this release, but not really one big feature - lots of new devices, plus a lot of cleanup and modernisation work spread throughout the subsystem: - More factoring out of common operations into helper functions by Morimoto-san. - DT schema conversons and stylistic nits. - Continued work on building out the new SOF IPC4 scheme. - Support for Awinc AT88395, Infineon PEB2466, Iron Device SMA1303, Mediatek MT8188, Realtek RT712, Renesas IDT821034, Samsung/Tesla FSD SoC I2S, and TI TAS5720A-Q1.
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h4
-rw-r--r--sound/soc/sof/amd/acp-ipc.c8
-rw-r--r--sound/soc/sof/amd/acp-loader.c7
-rw-r--r--sound/soc/sof/amd/acp.h7
-rw-r--r--sound/soc/sof/compress.c9
-rw-r--r--sound/soc/sof/control.c44
-rw-r--r--sound/soc/sof/core.c4
-rw-r--r--sound/soc/sof/intel/Kconfig4
-rw-r--r--sound/soc/sof/intel/cnl.c2
-rw-r--r--sound/soc/sof/intel/hda-dai.c92
-rw-r--r--sound/soc/sof/intel/hda-dsp.c46
-rw-r--r--sound/soc/sof/intel/hda-ipc.c36
-rw-r--r--sound/soc/sof/intel/hda.h6
-rw-r--r--sound/soc/sof/intel/mtl.c16
-rw-r--r--sound/soc/sof/intel/mtl.h6
-rw-r--r--sound/soc/sof/ipc3-control.c46
-rw-r--r--sound/soc/sof/ipc3-pcm.c3
-rw-r--r--sound/soc/sof/ipc3-topology.c39
-rw-r--r--sound/soc/sof/ipc3.c22
-rw-r--r--sound/soc/sof/ipc4-control.c33
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-pcm.c573
-rw-r--r--sound/soc/sof/ipc4-priv.h17
-rw-r--r--sound/soc/sof/ipc4-topology.c212
-rw-r--r--sound/soc/sof/ipc4-topology.h60
-rw-r--r--sound/soc/sof/ipc4.c29
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c22
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h10
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c8
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h2
-rw-r--r--sound/soc/sof/nocodec.c6
-rw-r--r--sound/soc/sof/ops.h18
-rw-r--r--sound/soc/sof/pcm.c36
-rw-r--r--sound/soc/sof/pm.c12
-rw-r--r--sound/soc/sof/sof-audio.c281
-rw-r--r--sound/soc/sof/sof-audio.h67
-rw-r--r--sound/soc/sof/sof-priv.h30
-rw-r--r--sound/soc/sof/stream-ipc.c53
-rw-r--r--sound/soc/sof/topology.c309
-rw-r--r--sound/soc/sof/trace.c8
40 files changed, 1787 insertions, 555 deletions
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index de5726251dc6..920155dee819 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -85,4 +85,8 @@
#define ACP_SCRATCH_REG_0 0x10000
#define ACP6X_DSP_FUSION_RUNSTALL 0x0644
+
+/* Cache window registers */
+#define ACP_DSP0_CACHE_OFFSET0 0x0420
+#define ACP_DSP0_CACHE_SIZE0 0x0424
#endif
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 5a02753c4610..4e0c48a36159 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -200,14 +200,15 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
}
EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
-int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
unsigned int offset = sdev->dsp_box.offset;
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
acp_mailbox_read(sdev, offset, p, sz);
} else {
+ struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream = substream->runtime->private_data;
if (!stream)
@@ -221,9 +222,10 @@ int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *sub
EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
+ struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream = substream->runtime->private_data;
/* check for unaligned offset or overflow */
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
index 090c8b18c83c..a4bce5a3ae48 100644
--- a/sound/soc/sof/amd/acp-loader.c
+++ b/sound/soc/sof/amd/acp-loader.c
@@ -151,6 +151,7 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev
int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata;
unsigned int src_addr, size_fw;
u32 page_count, dma_size;
@@ -183,6 +184,12 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
if (ret < 0)
dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
+ if (desc->rev > 3) {
+ /* Cache Window enable */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0, desc->sram_pte_offset);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0, SRAM1_SIZE | BIT(31));
+ }
+
/* Free memory once DMA is complete */
dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 09e16ef8afa0..39165ebf684b 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -12,6 +12,7 @@
#define __SOF_AMD_ACP_H
#include "../sof-priv.h"
+#include "../sof-audio.h"
#define ACP_MAX_STREAM 8
@@ -72,6 +73,8 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define AMD_STACK_DUMP_SIZE 32
+#define SRAM1_SIZE 0x13A000
+
enum clock_source {
ACP_CLOCK_96M = 0,
ACP_CLOCK_48M,
@@ -209,10 +212,10 @@ int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_ty
/* IPC callbacks */
irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context);
-int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg);
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 8e1a9ba111ad..8d205eb16d2f 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -10,6 +10,7 @@
#include "sof-audio.h"
#include "sof-priv.h"
#include "sof-utils.h"
+#include "ops.h"
static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,
u64 host_pos, u64 buffer_size)
@@ -237,6 +238,14 @@ static int sof_compr_set_params(struct snd_soc_component *component,
goto out;
}
+ ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[cstream->direction],
+ ipc_params_reply.posn_offset);
+ if (ret < 0) {
+ dev_err(component->dev, "Invalid stream data offset for Compr %d\n",
+ spcm->pcm.pcm_id);
+ goto out;
+ }
+
sstream->sampling_rate = params->codec.sample_rate;
sstream->channels = params->codec.ch_out;
sstream->sample_container_bytes = pcm->params.sample_container_bytes;
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index e0e9efd25d34..75e13f4fd1eb 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -22,9 +22,9 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->volume_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get)
return tplg_ops->control->volume_get(scontrol, ucontrol);
return 0;
@@ -37,9 +37,9 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->volume_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put)
return tplg_ops->control->volume_put(scontrol, ucontrol);
return false;
@@ -74,9 +74,9 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->switch_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get)
return tplg_ops->control->switch_get(scontrol, ucontrol);
return 0;
@@ -89,9 +89,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->switch_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put)
return tplg_ops->control->switch_put(scontrol, ucontrol);
return false;
@@ -104,9 +104,9 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = se->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->enum_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get)
return tplg_ops->control->enum_get(scontrol, ucontrol);
return 0;
@@ -119,9 +119,9 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = se->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->enum_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put)
return tplg_ops->control->enum_put(scontrol, ucontrol);
return false;
@@ -134,9 +134,9 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get)
return tplg_ops->control->bytes_get(scontrol, ucontrol);
return 0;
@@ -149,9 +149,9 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put)
return tplg_ops->control->bytes_put(scontrol, ucontrol);
return 0;
@@ -165,13 +165,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
/* make sure we have at least a header */
if (size < sizeof(struct snd_ctl_tlv))
return -EINVAL;
- if (tplg_ops->control->bytes_ext_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_put)
return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size);
return 0;
@@ -184,7 +184,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
int ret, err;
ret = pm_runtime_resume_and_get(scomp->dev);
@@ -193,7 +193,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
return ret;
}
- if (tplg_ops->control->bytes_ext_volatile_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get)
ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
pm_runtime_mark_last_busy(scomp->dev);
@@ -212,9 +212,9 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_ext_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get)
return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size);
return 0;
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 625977a29d8a..7de8673a01ce 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -362,6 +362,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->first_boot = true;
dev_set_drvdata(dev, sdev);
+ if (sof_core_debug)
+ dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
+
/* check IPC support */
if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
@@ -387,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->pcm_list);
INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list);
+ INIT_LIST_HEAD(&sdev->pipeline_list);
INIT_LIST_HEAD(&sdev->dai_list);
INIT_LIST_HEAD(&sdev->dai_link_list);
INIT_LIST_HEAD(&sdev->route_list);
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 36a0e2bf30ff..715ba8a7f2f8 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -322,8 +322,8 @@ config SND_SOC_SOF_HDA_PROBES
config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
tristate
- select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE
- select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE
+ select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n
+ select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE != n
config SND_SOC_SOF_INTEL_SOUNDWIRE
tristate "SOF support for SoundWire"
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 6b075bbe5bfb..a08a77fa946b 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -280,6 +280,8 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 1c3d4887aa30..193b3e74820a 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
{
struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
struct snd_soc_pcm_runtime *rtd;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
w = snd_soc_dai_get_widget(dai, substream->stream);
swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_stream_start(hext_stream);
+ if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ }
+
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
}
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
.shutdown = ssp_dai_shutdown,
};
-static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream)
-{
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
- struct snd_sof_dev *sdev;
- int ret;
-
- w = snd_soc_dai_get_widget(dai, stream);
- swidget = w->dobj.private;
- pipe_widget = swidget->pipe_widget;
- pipeline = pipe_widget->private;
- sdev = snd_soc_component_get_drvdata(swidget->scomp);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RESET);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_RESET;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- return ipc4_be_dai_common_trigger(dai, cmd, substream->stream);
-}
-
-static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = {
- .trigger = ipc4_be_dai_trigger,
-};
-
-static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = {
- .trigger = ipc4_be_dai_trigger,
-};
-
void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
int i;
@@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
for (i = 0; i < ops->num_drv; i++) {
- if (strstr(ops->drv[i].name, "DMIC")) {
- ops->drv[i].ops = &ipc4_dmic_dai_ops;
- continue;
- }
- if (strstr(ops->drv[i].name, "SSP")) {
- ops->drv[i].ops = &ipc4_ssp_dai_ops;
- continue;
- }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
if (strstr(ops->drv[i].name, "iDisp") ||
strstr(ops->drv[i].name, "Analog") ||
@@ -804,9 +749,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
if (!hda_use_tplg_nhlt)
ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE))
- sdw_callback.trigger = ipc4_be_dai_common_trigger;
-
break;
}
default:
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index b4eacae8564c..68eb06f13a1f 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -364,19 +364,12 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
{
- struct sof_ipc_pm_gate pm_gate;
- struct sof_ipc_reply reply;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- memset(&pm_gate, 0, sizeof(pm_gate));
+ if (pm_ops && pm_ops->set_pm_gate)
+ return pm_ops->set_pm_gate(sdev, flags);
- /* configure pm_gate ipc message */
- pm_gate.hdr.size = sizeof(pm_gate);
- pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
- pm_gate.flags = flags;
-
- /* send pm_gate ipc to dsp */
- return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
- &reply, sizeof(reply));
+ return 0;
}
static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
@@ -412,6 +405,34 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
return 0;
}
+/*
+ * d0i3 streaming is enabled if all the active streams can
+ * work in d0i3 state and playback is enabled
+ */
+static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_sof_pcm *spcm;
+ bool playback_active = false;
+ int dir;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ substream = spcm->stream[dir].substream;
+ if (!substream || !substream->runtime)
+ continue;
+
+ if (!spcm->stream[dir].d0i3_compatible)
+ return false;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ playback_active = true;
+ }
+ }
+
+ return playback_active;
+}
+
static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
const struct sof_dsp_power_state *target_state)
{
@@ -453,6 +474,9 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
!hda_enable_trace_D0I3_S0 ||
sdev->system_suspend_target != SOF_SUSPEND_NONE)
flags = HDA_PM_NO_DMA_TRACE;
+
+ if (hda_dsp_d0i3_streaming_applicable(sdev))
+ flags |= HDA_PM_PG_STREAMING;
} else {
/* prevent power gating in D0I0 */
flags = HDA_PM_PPG;
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index a7c454e03952..df541b22b2d2 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -67,6 +67,32 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
+static inline bool hda_dsp_ipc4_pm_msg(u32 primary)
+{
+ /* pm setting is only supported by module msg */
+ if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)
+ return false;
+
+ if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||
+ SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)
+ return true;
+
+ return false;
+}
+
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg)
+{
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ /* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */
+ if (hda_dsp_ipc4_pm_msg(msg_data->primary))
+ return;
+
+ mod_delayed_work(system_wq, &hdev->d0i3_work,
+ msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
+}
+
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
@@ -88,6 +114,8 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
@@ -361,12 +389,13 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
}
int hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
@@ -385,9 +414,10 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev,
}
int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index caccaf8fba9c..45f9d4248f14 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -656,10 +656,10 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
int enable, u32 size);
int hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
/*
@@ -919,6 +919,8 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev);
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg);
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
void hda_ipc4_dump(struct snd_sof_dev *sdev);
extern struct sdw_intel_ops sdw_callback;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 904ae42534e1..307faad2ecf4 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -110,6 +110,8 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms
snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR,
msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
@@ -581,6 +583,18 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
return mtl_enable_interrupts(sdev, false);
}
+static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ u32 llp_l, llp_u;
+
+ llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index));
+ llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index));
+ return ((u64)llp_u << 32) | llp_l;
+}
+
/* Meteorlake ops */
struct snd_sof_dsp_ops sof_mtl_ops;
EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -619,6 +633,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
/* dsp core get/put */
/* TODO: add core_get and core_put */
+ sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
+
sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 0fd4e6fe09b8..26418fb08807 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -6,6 +6,12 @@
* Copyright(c) 2020-2022 Intel Corporation. All rights reserved.
*/
+/* HDA Registers */
+#define MTL_PPLCLLPL_BASE 0x948
+#define MTL_PPLCLLPU_STRIDE 0x10
+#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE)
+#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE)
+
/* DSP Registers */
#define MTL_HFDSSCS 0x1000
#define MTL_HFDSSCS_SPA_MASK BIT(16)
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index 3fdc0d854e65..217ac5501a98 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -12,7 +12,8 @@
#include "ipc3-priv.h"
/* IPC set()/get() for kcontrols. */
-static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ bool set, bool lock)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
@@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct snd_sof_widget *swidget;
bool widget_found = false;
u32 ipc_cmd, msg_bytes;
+ int ret = 0;
list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->comp_id == scontrol->comp_id) {
@@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -EINVAL;
}
+ if (lock)
+ mutex_lock(&swidget->setup_mutex);
+ else
+ lockdep_assert_held(&swidget->setup_mutex);
+
/*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
+ * Volatile controls should always be part of static pipelines and the
+ * widget use_count would always be > 0 in this case. For the others,
+ * just return the cached value if the widget is not set up.
*/
if (!swidget->use_count)
- return 0;
+ goto unlock;
/*
* Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
@@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
sizeof(struct sof_abi_hdr);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
cdata->rhdr.hdr.size = msg_bytes;
cdata->elems_remaining = 0;
- return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+ ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+
+unlock:
+ if (lock)
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
}
static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */
scontrol->comp_data_dirty = false;
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
@@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
/* notify DSP of enum updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set enum updates for %s\n",
@@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0;
}
@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0;
}
@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0)
return ret;
@@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
/* set kcontrol data in DSP */
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false);
if (ret < 0) {
dev_err(sdev->dev,
"kcontrol %d set up failed for widget %s\n",
@@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
if (swidget->dynamic_pipeline_widget)
continue;
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);
if (ret < 0)
dev_warn(sdev->dev,
"kcontrol %d read failed for widget %s\n",
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index f10bfc9bd5cb..b29d93e0d216 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -129,7 +129,8 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset);
+ ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream],
+ ipc_params_reply.posn_offset);
if (ret < 0) {
dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n",
__func__, spcm->pcm.pcm_id);
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index b94cc40485ed..dceb78bfe17c 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -1651,6 +1651,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
struct sof_ipc_ctrl_data *cdata;
+ size_t priv_size_check;
int ret;
if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) {
@@ -1694,8 +1695,10 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
goto err;
}
- if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) {
- dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n");
+ priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr);
+ if (priv_size_check != scontrol->priv_size) {
+ dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n",
+ priv_size_check, scontrol->priv_size);
ret = -EINVAL;
goto err;
}
@@ -2230,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
return ret;
}
- swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget);
- if (swidget->complete < 0)
- return swidget->complete;
+ swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);
+ if (swidget->spipe->complete < 0)
+ return swidget->spipe->complete;
break;
default:
break;
@@ -2261,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
- if (!substream || !substream->runtime)
+ if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
@@ -2313,8 +2316,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
/* Do not free widgets for static pipelines with FW older than SOF2.2 */
if (!verify && !swidget->dynamic_pipeline_widget &&
SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
+ mutex_lock(&swidget->setup_mutex);
swidget->use_count = 0;
- swidget->complete = 0;
+ mutex_unlock(&swidget->setup_mutex);
+ if (swidget->spipe)
+ swidget->spipe->complete = 0;
continue;
}
@@ -2423,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
return 0;
}
+static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+ if (link->no_pcm)
+ return 0;
+
+ /*
+ * set default trigger order for all links. Exceptions to
+ * the rule will be handled in sof_pcm_dai_link_fixup()
+ * For playback, the sequence is the following: start FE,
+ * start BE, stop BE, stop FE; for Capture the sequence is
+ * inverted start BE, start FE, stop FE, stop BE
+ */
+ link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE;
+ link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST;
+
+ return 0;
+}
+
/* token list for each topology object */
static enum sof_tokens host_token_list[] = {
SOF_CORE_TOKENS,
@@ -2534,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
.parse_manifest = sof_ipc3_parse_manifest,
+ .link_setup = sof_ipc3_link_setup,
};
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 1fef4dcc0936..3de64ea2dc9a 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -847,7 +847,7 @@ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return;
@@ -882,7 +882,7 @@ static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
return;
@@ -1077,10 +1077,28 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
}
+static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc_pm_gate pm_gate;
+ struct sof_ipc_reply reply;
+
+ memset(&pm_gate, 0, sizeof(pm_gate));
+
+ /* configure pm_gate ipc message */
+ pm_gate.hdr.size = sizeof(pm_gate);
+ pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
+ pm_gate.flags = flags;
+
+ /* send pm_gate ipc to dsp */
+ return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
+ &reply, sizeof(reply));
+}
+
static const struct sof_ipc_pm_ops ipc3_pm_ops = {
.ctx_save = sof_ipc3_ctx_save,
.ctx_restore = sof_ipc3_ctx_restore,
.set_core_state = sof_ipc3_set_core_state,
+ .set_pm_gate = sof_ipc3_set_pm_gate,
};
const struct sof_ipc_ops ipc3_ops = {
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 0d5a578c3496..67bd2233fd9a 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -12,7 +12,8 @@
#include "ipc4-priv.h"
#include "ipc4-topology.h"
-static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ bool set, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct sof_ipc4_msg *msg = &cdata->msg;
struct snd_sof_widget *swidget;
bool widget_found = false;
+ int ret = 0;
/* find widget associated with the control */
list_for_each_entry(swidget, &sdev->widget_list, list) {
@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -ENOENT;
}
+ if (lock)
+ mutex_lock(&swidget->setup_mutex);
+ else
+ lockdep_assert_held(&swidget->setup_mutex);
+
/*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
+ * Volatile controls should always be part of static pipelines and the
+ * widget use_count would always be > 0 in this case. For the others,
+ * just return the cached value if the widget is not set up.
*/
if (!swidget->use_count)
- return 0;
+ goto unlock;
msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
- return iops->set_get_data(sdev, msg, msg->data_size, set);
+ ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+
+unlock:
+ if (lock)
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
}
static int
sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
- struct snd_sof_control *scontrol)
+ struct snd_sof_control *scontrol, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_gain *gain = swidget->private;
@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
msg->data_ptr = &data;
msg->data_size = sizeof(data);
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true);
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
msg->data_ptr = NULL;
msg->data_size = 0;
if (ret < 0) {
@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
return false;
}
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
if (ret < 0)
return false;
@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
if (ret < 0) {
dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
__func__, scontrol->comp_id, swidget->widget->name);
diff --git a/sound/soc/sof/ipc4-fw-reg.h b/sound/soc/sof/ipc4-fw-reg.h
new file mode 100644
index 000000000000..7226161e57e1
--- /dev/null
+++ b/sound/soc/sof/ipc4-fw-reg.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __IPC4_FW_REG_H__
+#define __IPC4_FW_REG_H__
+
+#define SOF_IPC4_INVALID_STREAM_POSITION ULLONG_MAX
+
+/**
+ * struct sof_ipc4_pipeline_registers - Pipeline start and end information in fw
+ * @stream_start_offset: Stream start offset (LPIB) reported by mixin
+ * module allocated on pipeline attached to Host Output Gateway when
+ * first data is being mixed to mixout module. When data is not mixed
+ * (right after creation/after reset) value "(u64)-1" is reported
+ * @stream_end_offset: Stream end offset (LPIB) reported by mixin
+ * module allocated on pipeline attached to Host Output Gateway
+ * during transition from RUNNING to PAUSED. When data is not mixed
+ * (right after creation or after reset) value "(u64)-1" is reported.
+ * When first data is mixed then value "0"is reported.
+ */
+struct sof_ipc4_pipeline_registers {
+ u64 stream_start_offset;
+ u64 stream_end_offset;
+} __packed __aligned(4);
+
+#define SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS 8
+
+/**
+ * struct sof_ipc4_peak_volume_regs - Volume information in fw
+ * @peak_meter: Peak volume value in fw
+ * @current_volume: Current volume value in fw
+ * @target_volume: Target volume value in fw
+ */
+struct sof_ipc4_peak_volume_regs {
+ u32 peak_meter[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+ u32 current_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+ u32 target_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+} __packed __aligned(4);
+
+/**
+ * struct sof_ipc4_llp_reading - Llp information in fw
+ * @llp_l: Lower part of 64-bit LLP
+ * @llp_u: Upper part of 64-bit LLP
+ * @wclk_l: Lower part of 64-bit Wallclock
+ * @wclk_u: Upper part of 64-bit Wallclock
+ */
+struct sof_ipc4_llp_reading {
+ u32 llp_l;
+ u32 llp_u;
+ u32 wclk_l;
+ u32 wclk_u;
+} __packed __aligned(4);
+
+/**
+ * struct of sof_ipc4_llp_reading_extended - Extended llp info
+ * @llp_reading: Llp information in memory window
+ * @tpd_low: Total processed data (low part)
+ * @tpd_high: Total processed data (high part)
+ */
+struct sof_ipc4_llp_reading_extended {
+ struct sof_ipc4_llp_reading llp_reading;
+ u32 tpd_low;
+ u32 tpd_high;
+} __packed __aligned(4);
+
+/**
+ * struct sof_ipc4_llp_reading_slot - Llp slot information in memory window
+ * @node_id: Dai gateway node id
+ * @reading: Llp information in memory window
+ */
+struct sof_ipc4_llp_reading_slot {
+ u32 node_id;
+ struct sof_ipc4_llp_reading reading;
+} __packed __aligned(4);
+
+/* ROM information */
+#define SOF_IPC4_FW_FUSE_VALUE_MASK GENMASK(7, 0)
+#define SOF_IPC4_FW_LOAD_METHOD_MASK BIT(8)
+#define SOF_IPC4_FW_DOWNLINK_IPC_USE_DMA_MASK BIT(9)
+#define SOF_IPC4_FW_LOAD_METHOD_REV_MASK GENMASK(11, 10)
+#define SOF_IPC4_FW_REVISION_MIN_MASK GENMASK(15, 12)
+#define SOF_IPC4_FW_REVISION_MAJ_MASK GENMASK(19, 16)
+#define SOF_IPC4_FW_VERSION_MIN_MASK GENMASK(23, 20)
+#define SOF_IPC4_FW_VERSION_MAJ_MASK GENMASK(27, 24)
+
+/* Number of dsp core supported in FW Regs. */
+#define SOF_IPC4_MAX_SUPPORTED_ADSP_CORES 8
+
+/* Number of host pipeline registers slots in FW Regs. */
+#define SOF_IPC4_MAX_PIPELINE_REG_SLOTS 16
+
+/* Number of PeakVol registers slots in FW Regs. */
+#define SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS 16
+
+/* Number of GPDMA LLP Reading slots in FW Regs. */
+#define SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS 24
+
+/* Number of Aggregated SNDW Reading slots in FW Regs. */
+#define SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS 15
+
+/* Current ABI version of the Fw registers layout. */
+#define SOF_IPC4_FW_REGS_ABI_VER 1
+
+/**
+ * struct sof_ipc4_fw_registers - FW Registers exposes additional
+ * DSP / FW state information to the driver
+ * @fw_status: Current ROM / FW status
+ * @lec: Last ROM / FW error code
+ * @fps: Current DSP clock status
+ * @lnec: Last Native Error Code(from external library)
+ * @ltr: Copy of LTRC HW register value(FW only)
+ * @rsvd0: Reserved0
+ * @rom_info: ROM info
+ * @abi_ver: Version of the layout, set to the current FW_REGS_ABI_VER
+ * @slave_core_sts: Slave core states
+ * @rsvd2: Reserved2
+ * @pipeline_regs: State of pipelines attached to host output gateways
+ * @peak_vol_regs: State of PeakVol instances indexed by the PeakVol's instance_id
+ * @llp_gpdma_reading_slots: LLP Readings for single link gateways
+ * @llp_sndw_reading_slots: SNDW aggregated link gateways
+ * @llp_evad_reading_slot: LLP Readings for EVAD gateway
+ */
+struct sof_ipc4_fw_registers {
+ u32 fw_status;
+ u32 lec;
+ u32 fps;
+ u32 lnec;
+ u32 ltr;
+ u32 rsvd0;
+ u32 rom_info;
+ u32 abi_ver;
+ u8 slave_core_sts[SOF_IPC4_MAX_SUPPORTED_ADSP_CORES];
+ u32 rsvd2[6];
+
+ struct sof_ipc4_pipeline_registers
+ pipeline_regs[SOF_IPC4_MAX_PIPELINE_REG_SLOTS];
+
+ struct sof_ipc4_peak_volume_regs
+ peak_vol_regs[SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot
+ llp_gpdma_reading_slots[SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot
+ llp_sndw_reading_slots[SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot llp_evad_reading_slot;
+} __packed __aligned(4);
+
+#endif
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 96941bebc1f1..68258767aace 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -10,8 +10,37 @@
#include <sound/sof/ipc4/header.h>
#include "sof-audio.h"
#include "sof-priv.h"
+#include "ops.h"
#include "ipc4-priv.h"
#include "ipc4-topology.h"
+#include "ipc4-fw-reg.h"
+
+static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct sof_ipc4_msg msg = {{ 0 }};
+ u32 primary, ipc_size;
+
+ /* trigger a single pipeline */
+ if (trigger_list->count == 1)
+ return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state);
+
+ primary = state;
+ primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
+ primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+ msg.primary = primary;
+
+ /* trigger multiple pipelines with a single IPC */
+ msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
+
+ /* ipc_size includes the count and the pipeline IDs for the number of pipelines */
+ ipc_size = sizeof(u32) * (trigger_list->count + 1);
+ msg.data_size = ipc_size;
+ msg.data_ptr = trigger_list;
+
+ return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+}
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
{
@@ -32,80 +61,238 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
}
EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
+static void
+sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
+ struct snd_sof_pipeline *spipe,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->skip_during_fe_trigger)
+ return;
+
+ switch (state) {
+ case SOF_IPC4_PIPE_RUNNING:
+ /*
+ * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING
+ * for the first time
+ */
+ if (spipe->started_count == spipe->paused_count)
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ case SOF_IPC4_PIPE_RESET:
+ /* RESET if the pipeline is neither running nor paused */
+ if (!spipe->started_count && !spipe->paused_count)
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ case SOF_IPC4_PIPE_PAUSED:
+ /* Pause the pipeline only when its started_count is 1 more than paused_count */
+ if (spipe->paused_count == (spipe->started_count - 1))
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
+ struct snd_sof_pipeline *spipe,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ int i;
+
+ if (pipeline->skip_during_fe_trigger)
+ return;
+
+ /* set state for pipeline if it was just triggered */
+ for (i = 0; i < trigger_list->count; i++) {
+ if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
+ pipeline->state = state;
+ break;
+ }
+ }
+
+ switch (state) {
+ case SOF_IPC4_PIPE_PAUSED:
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /*
+ * increment paused_count if the PAUSED is the final state during
+ * the PAUSE trigger
+ */
+ spipe->paused_count++;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /*
+ * decrement started_count if PAUSED is the final state during the
+ * STOP trigger
+ */
+ spipe->started_count--;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SOF_IPC4_PIPE_RUNNING:
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* decrement paused_count for RELEASE */
+ spipe->paused_count--;
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* increment started_count for START/RESUME */
+ spipe->started_count++;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * The picture below represents the pipeline state machine wrt PCM actions corresponding to the
+ * triggers and ioctls
+ * +---------------+
+ * | |
+ * | INIT |
+ * | |
+ * +-------+-------+
+ * |
+ * |
+ * | START
+ * |
+ * |
+ * +----------------+ +------v-------+ +-------------+
+ * | | START | | HW_FREE | |
+ * | RUNNING <-------------+ PAUSED +--------------> + RESET |
+ * | | PAUSE | | | |
+ * +------+---------+ RELEASE +---------+----+ +-------------+
+ * | ^
+ * | |
+ * | |
+ * | |
+ * | PAUSE |
+ * +---------------------------------+
+ * STOP/SUSPEND
+ *
+ * Note that during system suspend, the suspend trigger is followed by a hw_free in
+ * sof_pcm_trigger(). So, the final state during suspend would be RESET.
+ * Also, since the SOF driver doesn't support full resume, streams would be restarted with the
+ * prepare ioctl before the START trigger.
+ */
+
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
- struct snd_pcm_substream *substream, int state)
+ struct snd_pcm_substream *substream, int state, int cmd)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_sof_widget *pipeline_widget;
- struct snd_soc_dapm_widget_list *list;
- struct snd_soc_dapm_widget *widget;
- struct sof_ipc4_pipeline *pipeline;
- struct snd_sof_widget *swidget;
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct ipc4_pipeline_set_state_data *trigger_list;
+ struct snd_sof_pipeline *spipe;
struct snd_sof_pcm *spcm;
- int ret = 0;
- int num_widgets;
+ int ret;
+ int i;
+
+ dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm)
return -EINVAL;
- list = spcm->stream[substream->stream].list;
+ pipeline_list = &spcm->stream[substream->stream].pipeline_list;
- for_each_dapm_widgets(list, num_widgets, widget) {
- swidget = widget->dobj.private;
+ /* nothing to trigger if the list is empty */
+ if (!pipeline_list->pipelines || !pipeline_list->count)
+ return 0;
- if (!swidget)
- continue;
+ /* allocate memory for the pipeline data */
+ trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
+ GFP_KERNEL);
+ if (!trigger_list)
+ return -ENOMEM;
- /*
- * set pipeline state for both FE and BE pipelines for RUNNING state.
- * For PAUSE/RESET, set the pipeline state only for the FE pipeline.
- */
- switch (state) {
- case SOF_IPC4_PIPE_PAUSED:
- case SOF_IPC4_PIPE_RESET:
- if (!WIDGET_IS_AIF(swidget->id))
- continue;
- break;
- default:
- break;
- }
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
- /* find pipeline widget for the pipeline that this widget belongs to */
- pipeline_widget = swidget->pipe_widget;
- pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
+ /*
+ * IPC4 requires pipelines to be triggered in order starting at the sink and
+ * walking all the way to the source. So traverse the pipeline_list in the order
+ * sink->source when starting PCM's and in the reverse order to pause/stop PCM's.
+ * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork
+ * in the pipeline, the order of triggering between the left/right paths will be
+ * indeterministic. But the sink->source trigger order sink->source would still be
+ * guaranteed for each fork independently.
+ */
+ if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
+ for (i = pipeline_list->count - 1; i >= 0; i--) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+ }
+ else
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+ }
- if (pipeline->state == state)
- continue;
+ /* return if all pipelines are in the requested state already */
+ if (!trigger_list->count) {
+ ret = 0;
+ goto free;
+ }
- /* first set the pipeline to PAUSED state */
- if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to pause pipeline %d\n",
- swidget->pipeline_id);
- return ret;
- }
- }
+ /* no need to pause before reset or before pause release */
+ if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+ goto skip_pause_transition;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ /*
+ * set paused state for pipelines if the final state is PAUSED or when the pipeline
+ * is set to RUNNING for the first time after the PCM is started.
+ */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to pause all pipelines\n");
+ goto free;
+ }
- if (pipeline->state == state)
- continue;
+ /* update PAUSED state for all pipelines just triggered */
+ for (i = 0; i < pipeline_list->count ; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
+ trigger_list);
+ }
- /* then set the final state */
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
- state, swidget->pipeline_id);
- break;
- }
+ /* return if this is the final state */
+ if (state == SOF_IPC4_PIPE_PAUSED)
+ goto free;
+skip_pause_transition:
+ /* else set the RUNNING/RESET state in the DSP */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
+ goto free;
+ }
- pipeline->state = state;
+ /* update RUNNING/RESET state for all pipelines that were just triggered */
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
}
+free:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ kfree(trigger_list);
return ret;
}
@@ -134,13 +321,14 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
}
/* set the pipeline state */
- return sof_ipc4_trigger_pipelines(component, substream, state);
+ return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
}
static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET);
+ /* command is not relevant with RESET, so just pass 0 */
+ return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
}
static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
@@ -183,7 +371,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc4_copier *ipc4_copier;
- struct snd_soc_dpcm *dpcm;
if (!dai) {
dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -205,17 +392,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
rate->max = rate->min;
- /*
- * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required
- * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI
- * pipeline gets triggered and the pipeline widgets are freed.
- */
- for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
- struct snd_soc_pcm_runtime *fe = dpcm->fe;
-
- fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
- }
-
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_SSP:
ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
@@ -227,8 +403,273 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
+static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ int stream;
+
+ for_each_pcm_streams(stream) {
+ pipeline_list = &spcm->stream[stream].pipeline_list;
+ kfree(pipeline_list->pipelines);
+ pipeline_list->pipelines = NULL;
+ kfree(spcm->stream[stream].private);
+ spcm->stream[stream].private = NULL;
+ }
+}
+
+static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_timestamp_info *stream_info;
+ bool support_info = true;
+ u32 abi_version;
+ u32 abi_offset;
+ int stream;
+
+ abi_offset = offsetof(struct sof_ipc4_fw_registers, abi_ver);
+ sof_mailbox_read(sdev, sdev->fw_info_box.offset + abi_offset, &abi_version,
+ sizeof(abi_version));
+
+ if (abi_version < SOF_IPC4_FW_REGS_ABI_VER)
+ support_info = false;
+
+ for_each_pcm_streams(stream) {
+ pipeline_list = &spcm->stream[stream].pipeline_list;
+
+ /* allocate memory for max number of pipeline IDs */
+ pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines,
+ sizeof(struct snd_sof_widget *), GFP_KERNEL);
+ if (!pipeline_list->pipelines) {
+ sof_ipc4_pcm_free(sdev, spcm);
+ return -ENOMEM;
+ }
+
+ if (!support_info)
+ continue;
+
+ stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL);
+ if (!stream_info) {
+ sof_ipc4_pcm_free(sdev, spcm);
+ return -ENOMEM;
+ }
+
+ spcm->stream[stream].private = stream_info;
+ }
+
+ return 0;
+}
+
+static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm)
+{
+ struct sof_ipc4_copier *host_copier = NULL;
+ struct sof_ipc4_copier *dai_copier = NULL;
+ struct sof_ipc4_llp_reading_slot llp_slot;
+ struct sof_ipc4_timestamp_info *info;
+ struct snd_soc_dapm_widget *widget;
+ struct snd_sof_dai *dai;
+ int i;
+
+ /* find host & dai to locate info in memory window */
+ for_each_dapm_widgets(spcm->list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ if (WIDGET_IS_AIF(swidget->widget->id)) {
+ host_copier = swidget->private;
+ } else if (WIDGET_IS_DAI(swidget->widget->id)) {
+ dai = swidget->private;
+ dai_copier = dai->private;
+ }
+ }
+
+ /* both host and dai copier must be valid for time_info */
+ if (!host_copier || !dai_copier) {
+ dev_err(sdev->dev, "host or dai copier are not found\n");
+ return;
+ }
+
+ info = spcm->private;
+ info->host_copier = host_copier;
+ info->dai_copier = dai_copier;
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) +
+ sdev->fw_info_box.offset;
+
+ /* find llp slot used by current dai */
+ for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) {
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
+ break;
+
+ info->llp_offset += sizeof(llp_slot);
+ }
+
+ if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS)
+ return;
+
+ /* if no llp gpdma slot is used, check aggregated sdw slot */
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) +
+ sdev->fw_info_box.offset;
+ for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) {
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
+ break;
+
+ info->llp_offset += sizeof(llp_slot);
+ }
+
+ if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS)
+ return;
+
+ /* check EVAD slot */
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) +
+ sdev->fw_info_box.offset;
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) {
+ dev_info(sdev->dev, "no llp found, fall back to default HDA path");
+ info->llp_offset = 0;
+ }
+}
+
+static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_sof_platform_stream_params *platform_params)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_ipc4_timestamp_info *time_info;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ time_info = spcm->stream[substream->stream].private;
+ /* delay calculation is not supported by current fw_reg ABI */
+ if (!time_info)
+ return 0;
+
+ time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
+ time_info->llp_offset = 0;
+
+ sof_ipc4_build_time_info(sdev, &spcm->stream[substream->stream]);
+
+ return 0;
+}
+
+static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *stream,
+ struct sof_ipc4_timestamp_info *time_info)
+{
+ struct sof_ipc4_copier *host_copier = time_info->host_copier;
+ struct sof_ipc4_copier *dai_copier = time_info->dai_copier;
+ struct sof_ipc4_pipeline_registers ppl_reg;
+ u64 stream_start_position;
+ u32 dai_sample_size;
+ u32 ch, node_index;
+ u32 offset;
+
+ if (!host_copier || !dai_copier)
+ return -EINVAL;
+
+ if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID)
+ return -EINVAL;
+
+ node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id);
+ offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg);
+ sof_mailbox_read(sdev, sdev->fw_info_box.offset + offset, &ppl_reg, sizeof(ppl_reg));
+ if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION)
+ return -EINVAL;
+
+ stream_start_position = ppl_reg.stream_start_offset;
+ ch = dai_copier->data.out_format.fmt_cfg;
+ ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch);
+ dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch;
+ /* convert offset to sample count */
+ do_div(stream_start_position, dai_sample_size);
+ time_info->stream_start_offset = stream_start_position;
+
+ return 0;
+}
+
+static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_ipc4_timestamp_info *time_info;
+ struct sof_ipc4_llp_reading_slot llp;
+ snd_pcm_uframes_t head_ptr, tail_ptr;
+ struct snd_sof_pcm_stream *stream;
+ struct snd_sof_pcm *spcm;
+ u64 tmp_ptr;
+ int ret;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return 0;
+
+ stream = &spcm->stream[substream->stream];
+ time_info = stream->private;
+ if (!time_info)
+ return 0;
+
+ /*
+ * stream_start_offset is updated to memory window by FW based on
+ * pipeline statistics and it may be invalid if host query happens before
+ * the statistics is complete. And it will not change after the first initiailization.
+ */
+ if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) {
+ ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info);
+ if (ret < 0)
+ return 0;
+ }
+
+ /*
+ * HDaudio links don't support the LLP counter reported by firmware
+ * the link position is read directly from hardware registers.
+ */
+ if (!time_info->llp_offset) {
+ tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream);
+ if (!tmp_ptr)
+ return 0;
+ } else {
+ sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
+ tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
+ }
+
+ /* In two cases dai dma position is not accurate
+ * (1) dai pipeline is started before host pipeline
+ * (2) multiple streams mixed into one. Each stream has the same dai dma position
+ *
+ * Firmware calculates correct stream_start_offset for all cases including above two.
+ * Driver subtracts stream_start_offset from dai dma position to get accurate one
+ */
+ tmp_ptr -= time_info->stream_start_offset;
+
+ /* Calculate the delay taking into account that both pointer can wrap */
+ div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ head_ptr = substream->runtime->status->hw_ptr;
+ tail_ptr = tmp_ptr;
+ } else {
+ head_ptr = tmp_ptr;
+ tail_ptr = substream->runtime->status->hw_ptr;
+ }
+
+ if (head_ptr < tail_ptr)
+ return substream->runtime->boundary - tail_ptr + head_ptr;
+
+ return head_ptr - tail_ptr;
+}
+
const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
+ .hw_params = sof_ipc4_pcm_hw_params,
.trigger = sof_ipc4_pcm_trigger,
.hw_free = sof_ipc4_pcm_hw_free,
.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
+ .pcm_setup = sof_ipc4_pcm_setup,
+ .pcm_free = sof_ipc4_pcm_free,
+ .delay = sof_ipc4_pcm_delay
};
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index fc9efdce67e0..f461b8c70df3 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -14,6 +14,7 @@
#include "sof-priv.h"
/* The DSP window indices are fixed */
+#define SOF_IPC4_INBOX_WINDOW_IDX 0
#define SOF_IPC4_OUTBOX_WINDOW_IDX 1
#define SOF_IPC4_DEBUG_WINDOW_IDX 2
@@ -70,6 +71,7 @@ struct sof_ipc4_fw_library {
* base firmware
*
* @load_library: Callback function for platform dependent library loading
+ * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
*/
struct sof_ipc4_fw_data {
u32 manifest_fw_hdr_offset;
@@ -82,6 +84,21 @@ struct sof_ipc4_fw_data {
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
+ struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
+};
+
+/**
+ * struct sof_ipc4_timestamp_info - IPC4 timestamp info
+ * @host_copier: the host copier of the pcm stream
+ * @dai_copier: the dai copier of the pcm stream
+ * @stream_start_offset: reported by fw in memory window
+ * @llp_offset: llp offset in memory window
+ */
+struct sof_ipc4_timestamp_info {
+ struct sof_ipc4_copier *host_copier;
+ struct sof_ipc4_copier *dai_copier;
+ u64 stream_start_offset;
+ u32 llp_offset;
};
extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 59f4d42f9011..3e27c7a48ebd 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -354,6 +354,13 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
goto free_available_fmt;
}
+ /*
+ * This callback is used by host copier and module-to-module copier,
+ * and only host copier needs to set gtw_cfg.
+ */
+ if (!WIDGET_IS_AIF(swidget->id))
+ goto skip_gtw_cfg;
+
ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(u32),
@@ -380,7 +387,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
}
dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
- ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+skip_gtw_cfg:
ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
if (!ipc4_copier->gtw_attr) {
ret = -ENOMEM;
@@ -391,6 +398,21 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
ipc4_copier->data.gtw_cfg.config_length =
sizeof(struct sof_ipc4_gtw_attributes) >> 2;
+ switch (swidget->id) {
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+ break;
+ case snd_soc_dapm_buffer:
+ ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
+ ipc4_copier->ipc_config_size = 0;
+ break;
+ default:
+ dev_err(scomp->dev, "invalid widget type %d\n", swidget->id);
+ ret = -EINVAL;
+ goto free_gtw_attr;
+ }
+
/* set up module info and message header */
ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
if (ret)
@@ -833,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg
total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage += total;
}
@@ -947,11 +969,11 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
struct sof_ipc4_pipeline *pipeline;
/* reset pipeline memory usage */
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage = 0;
- if (WIDGET_IS_AIF(swidget->id)) {
+ if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
ipc4_copier = swidget->private;
} else if (WIDGET_IS_DAI(swidget->id)) {
struct snd_sof_dai *dai = swidget->private;
@@ -1114,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
gtw_attr = ipc4_copier->gtw_attr;
@@ -1177,6 +1199,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
break;
}
+ case snd_soc_dapm_buffer:
+ {
+ ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
+ copier_data = &ipc4_copier->data;
+ available_fmt = &ipc4_copier->available_fmt;
+
+ /*
+ * base_config->audio_fmt represent the input audio formats. Use
+ * the input format as the reference to match pcm params
+ */
+ available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
+ ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+ ref_params = pipeline_params;
+
+ break;
+ }
default:
dev_err(sdev->dev, "unsupported type %d for copier %s",
swidget->id, swidget->widget->name);
@@ -1203,8 +1241,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_copier_data *alh_data;
struct sof_ipc4_copier *alh_copier;
struct snd_sof_widget *w;
+ u32 ch_count = 0;
u32 ch_mask = 0;
u32 ch_map;
+ u32 step;
+ u32 mask;
int i;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
@@ -1214,11 +1255,15 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
/* Get channel_mask from ch_map */
ch_map = copier_data->base_config.audio_fmt.ch_map;
for (i = 0; ch_map; i++) {
- if ((ch_map & 0xf) != 0xf)
+ if ((ch_map & 0xf) != 0xf) {
ch_mask |= BIT(i);
+ ch_count++;
+ }
ch_map >>= 4;
}
+ step = ch_count / blob->alh_cfg.count;
+ mask = GENMASK(step - 1, 0);
/*
* Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
* for all widgets with the same stream name
@@ -1233,7 +1278,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
- blob->alh_cfg.mapping[i].channel_mask = ch_mask;
+ /*
+ * Set the same channel mask for playback as the audio data is
+ * duplicated for all speakers. For capture, split the channels
+ * among the aggregated DAIs. For example, with 4 channels on 2
+ * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the
+ * two DAI's.
+ * The channel masks used depend on the cpu_dais used in the
+ * dailink at the machine driver level, which actually comes from
+ * the tables in soc_acpi files depending on the _ADR and devID
+ * registers for each codec.
+ */
+ if (w->id == snd_soc_dapm_dai_in)
+ blob->alh_cfg.mapping[i].channel_mask = ch_mask;
+ else
+ blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);
+
i++;
}
if (blob->alh_cfg.count > 1) {
@@ -1435,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_pipeline *pipeline;
struct sof_ipc4_msg *msg;
@@ -1465,6 +1525,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
break;
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_buffer:
{
struct sof_ipc4_copier *ipc4_copier = swidget->private;
@@ -1564,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct sof_ipc4_fw_module *fw_module = swidget->module_info;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0;
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
/* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) {
struct sof_ipc4_pipeline *pipeline = swidget->private;
@@ -1591,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
ida_free(&fw_module->m_ida, swidget->instance_id);
}
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
@@ -1676,6 +1742,55 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
ida_free(queue_ida, queue_id);
}
+static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *src_widget,
+ struct snd_sof_widget *sink_widget,
+ int sink_id)
+{
+ struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
+ struct sof_ipc4_base_module_cfg *src_config;
+ struct sof_ipc4_copier_config_set_sink_format format;
+ struct sof_ipc4_fw_module *fw_module;
+ struct sof_ipc4_msg msg = {{ 0 }};
+ u32 header, extension;
+
+ dev_dbg(sdev->dev, "%s set copier sink %d format\n",
+ src_widget->widget->name, sink_id);
+
+ if (WIDGET_IS_DAI(src_widget->id)) {
+ struct snd_sof_dai *dai = src_widget->private;
+
+ src_config = dai->private;
+ } else {
+ src_config = src_widget->private;
+ }
+
+ fw_module = src_widget->module_info;
+
+ format.sink_id = sink_id;
+ memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
+ memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
+ msg.data_size = sizeof(format);
+ msg.data_ptr = &format;
+
+ header = fw_module->man4_module_entry.id;
+ header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
+ header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+ header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+ extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size);
+ extension |=
+ SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
+ extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
+ extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+
+ msg.primary = header;
+ msg.extension = extension;
+
+ return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
+}
+
static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
{
struct snd_sof_widget *src_widget = sroute->src_widget;
@@ -1704,6 +1819,17 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
return sroute->dst_queue_id;
}
+ /* Pin 0 format is already set during copier module init */
+ if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
+ ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
+ sroute->src_queue_id);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
+ src_widget->widget->name, sroute->src_queue_id);
+ goto out;
+ }
+ }
+
dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
@@ -1724,16 +1850,18 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
if (ret < 0) {
- dev_err(sdev->dev, "%s: failed to bind modules %s -> %s\n",
- __func__, src_widget->widget->name, sink_widget->widget->name);
-
- sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
- SOF_PIN_TYPE_SOURCE);
- sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id,
- SOF_PIN_TYPE_SINK);
+ dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
+ src_widget->widget->name, sroute->src_queue_id,
+ sink_widget->widget->name, sroute->dst_queue_id);
+ goto out;
}
return ret;
+
+out:
+ sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
+ sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
+ return ret;
}
static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
@@ -1744,12 +1872,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
struct sof_ipc4_msg msg = {{ 0 }};
u32 header, extension;
- int ret;
+ int ret = 0;
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
+ /*
+ * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
+ * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
+ */
+ if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
+ goto out;
+
header = src_fw_module->man4_module_entry.id;
header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
@@ -1766,9 +1901,10 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
if (ret < 0)
- dev_err(sdev->dev, "failed to unbind modules %s -> %s\n",
- src_widget->widget->name, sink_widget->widget->name);
-
+ dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
+ src_widget->widget->name, sroute->src_queue_id,
+ sink_widget->widget->name, sroute->dst_queue_id);
+out:
sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
@@ -1778,7 +1914,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
unsigned int flags, struct snd_sof_dai_config_data *data)
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_gtw_attributes *gtw_attr;
@@ -1801,6 +1937,7 @@ 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;
+ pipeline->skip_during_fe_trigger = true;
fallthrough;
case SOF_DAI_INTEL_ALH:
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
@@ -1957,7 +2094,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
- if (!substream || !substream->runtime)
+ if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
@@ -1970,7 +2107,25 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
return 0;
}
-static enum sof_tokens host_token_list[] = {
+static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+ if (link->no_pcm)
+ return 0;
+
+ /*
+ * set default trigger order for all links. Exceptions to
+ * the rule will be handled in sof_pcm_dai_link_fixup()
+ * For playback, the sequence is the following: start BE,
+ * start FE, stop FE, stop BE; for Capture the sequence is
+ * inverted start FE, start BE, stop BE, stop FE
+ */
+ link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
+ link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
+
+ return 0;
+}
+
+static enum sof_tokens common_copier_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
@@ -2026,12 +2181,12 @@ static enum sof_tokens src_token_list[] = {
static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
[snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
- host_token_list, ARRAY_SIZE(host_token_list), NULL,
- sof_ipc4_prepare_copier_module,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
- host_token_list, ARRAY_SIZE(host_token_list), NULL,
- sof_ipc4_prepare_copier_module,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
@@ -2041,6 +2196,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
+ [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
+ sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,
sof_ipc4_widget_free_comp_pipeline,
pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
@@ -2072,4 +2231,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.parse_manifest = sof_ipc4_parse_manifest,
.dai_get_clk = sof_ipc4_dai_get_clk,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
+ .link_setup = sof_ipc4_link_setup,
};
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 0aa87a8add5d..72529179ac22 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -53,6 +53,8 @@
#define ALH_MAX_NUMBER_OF_GTW 16
+#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
+
/*
* The base of multi-gateways. Multi-gateways addressing starts from
* ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
@@ -64,6 +66,52 @@
/* A magic number from FW */
#define ALH_MULTI_GTW_COUNT 8
+enum sof_ipc4_copier_module_config_params {
+/*
+ * Use LARGE_CONFIG_SET to initialize timestamp event. Ipc mailbox must
+ * contain properly built CopierConfigTimestampInitData struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_TIMESTAMP_INIT = 1,
+/*
+ * Use LARGE_CONFIG_SET to initialize copier sink. Ipc mailbox must contain
+ * properly built CopierConfigSetSinkFormat struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT,
+/*
+ * Use LARGE_CONFIG_SET to initialize and enable on Copier data segment
+ * event. Ipc mailbox must contain properly built DataSegmentEnabled struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_DATA_SEGMENT_ENABLED,
+/*
+ * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non
+ * HD-A gateways.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING,
+/*
+ * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non
+ * HD-A gateways and corresponding total processed data
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING_EXTENDED,
+/*
+ * Use LARGE_CONFIG_SET to setup attenuation on output pins. Data is just uint32_t.
+ * note Config is only allowed when output pin is set up for 32bit and source
+ * is connected to Gateway
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_ATTENUATION,
+};
+
+struct sof_ipc4_copier_config_set_sink_format {
+/* Id of sink */
+ u32 sink_id;
+/*
+ * Input format used by the source
+ * attention must be the same as present if already initialized.
+ */
+ struct sof_ipc4_audio_format source_fmt;
+/* Output format used by the sink */
+ struct sof_ipc4_audio_format sink_fmt;
+} __packed __aligned(4);
+
/**
* struct sof_ipc4_pipeline - pipeline config data
* @priority: Priority of this pipeline
@@ -71,6 +119,7 @@
* @mem_usage: Memory usage
* @state: Pipeline state
* @msg: message structure for pipeline
+ * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
*/
struct sof_ipc4_pipeline {
uint32_t priority;
@@ -78,9 +127,20 @@ struct sof_ipc4_pipeline {
uint32_t mem_usage;
int state;
struct sof_ipc4_msg msg;
+ bool skip_during_fe_trigger;
};
/**
+ * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data
+ * @count: Number of pipelines to be triggered
+ * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered
+ */
+struct ipc4_pipeline_set_state_data {
+ u32 count;
+ DECLARE_FLEX_ARRAY(u32, pipeline_ids);
+} __packed;
+
+/**
* struct sof_ipc4_available_audio_format - Available audio formats
* @base_config: Available base config
* @out_audio_fmt: Available output audio format
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 74cd7e956019..8ede4b952997 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -13,6 +13,7 @@
#include <sound/sof/ipc4/header.h>
#include "sof-priv.h"
#include "sof-audio.h"
+#include "ipc4-fw-reg.h"
#include "ipc4-priv.h"
#include "ops.h"
@@ -369,6 +370,17 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
if (!msg_data)
return -EINVAL;
+ if (!no_pm) {
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ /* ensure the DSP is in D0i0 before sending a new IPC */
+ ret = snd_sof_dsp_set_power_state(sdev, &target_state);
+ if (ret < 0)
+ return ret;
+ }
+
/* Serialise IPC TX */
mutex_lock(&ipc->tx_mutex);
@@ -542,6 +554,8 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg
outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+ sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX);
+ sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers);
sdev->dsp_box.offset = inbox_offset;
sdev->dsp_box.size = inbox_size;
sdev->host_box.offset = outbox_offset;
@@ -653,15 +667,30 @@ static int sof_ipc4_ctx_save(struct snd_sof_dev *sdev)
return sof_ipc4_set_core_state(sdev, SOF_DSP_PRIMARY_CORE, false);
}
+static int sof_ipc4_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc4_msg msg = {{0}};
+
+ msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_SET_D0IX);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.extension = flags;
+
+ return sof_ipc4_tx_msg(sdev, &msg, 0, NULL, 0, true);
+}
+
static const struct sof_ipc_pm_ops ipc4_pm_ops = {
.ctx_save = sof_ipc4_ctx_save,
.set_core_state = sof_ipc4_set_core_state,
+ .set_pm_gate = sof_ipc4_set_pm_gate,
};
static int sof_ipc4_init(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ mutex_init(&ipc4_data->pipeline_state_mutex);
+
xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
return 0;
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index 79da25725987..597cb4476acb 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -494,7 +494,7 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev,
}
stream = &spcm->stream[substream->stream];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return 0;
@@ -588,6 +588,9 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
.drv = mt8186_dai,
.num_drv = ARRAY_SIZE(mt8186_dai),
+ /* Debug information */
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
/* PM */
.suspend = mt8186_dsp_suspend,
.resume = mt8186_dsp_resume,
@@ -625,8 +628,25 @@ static const struct sof_dev_desc sof_of_mt8186_desc = {
.ops = &sof_mt8186_ops,
};
+static const struct sof_dev_desc sof_of_mt8188_desc = {
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "mediatek/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "mediatek/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-mt8188.ri",
+ },
+ .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg",
+ .ops = &sof_mt8186_ops,
+};
+
static const struct of_device_id sof_of_mt8186_ids[] = {
{ .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc},
+ { .compatible = "mediatek,mt8188-dsp", .data = &sof_of_mt8188_desc},
{ }
};
MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids);
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h
index 98b2965e5ba6..5b521c60b4e3 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.h
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.h
@@ -52,7 +52,15 @@ struct snd_sof_dev;
#define ADSP_PRID 0x0
#define ADSP_ALTVEC_C0 0x04
#define ADSP_ALTVECSEL 0x0C
-#define ADSP_ALTVECSEL_C0 BIT(1)
+#define MT8188_ADSP_ALTVECSEL_C0 BIT(0)
+#define MT8186_ADSP_ALTVECSEL_C0 BIT(1)
+
+/*
+ * On MT8188, BIT(1) is not evaluated and on MT8186 BIT(0) is not evaluated:
+ * We can simplify the driver by safely setting both bits regardless of the SoC.
+ */
+#define ADSP_ALTVECSEL_C0 (MT8188_ADSP_ALTVECSEL_C0 | \
+ MT8186_ADSP_ALTVECSEL_C0)
/* dsp bus */
#define ADSP_SRAM_POOL_CON 0x190
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 3c81e84fcecf..42bae574c87a 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -215,11 +215,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
adsp->pa_sram = (phys_addr_t)mmio->start;
adsp->sramsize = resource_size(mmio);
- if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) {
- dev_err(dev, "adsp SRAM(%#x) is not enough for share\n",
- adsp->sramsize);
- return -EINVAL;
- }
dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize);
@@ -525,7 +520,7 @@ static snd_pcm_uframes_t mt8195_pcm_pointer(struct snd_sof_dev *sdev,
}
stream = &spcm->stream[substream->stream];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return 0;
@@ -642,6 +637,7 @@ static struct snd_sof_dsp_ops sof_mt8195_ops = {
/* Debug information */
.dbg_dump = mt8195_adsp_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* DAI drivers */
.drv = mt8195_dai,
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h
index 7ffd523f936c..b4229170049f 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.h
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.h
@@ -139,8 +139,6 @@ struct snd_sof_dev;
#define DSP_MBOX1_BAR 6
#define DSP_MBOX2_BAR 7
-#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0
-
#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/
#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index 3537805070ad..b13bfdeb2b70 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -103,14 +103,8 @@ static int sof_nocodec_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(&pdev->dev, card);
}
-static int sof_nocodec_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver sof_nocodec_audio = {
.probe = sof_nocodec_probe,
- .remove = sof_nocodec_remove,
.driver = {
.name = "sof-nocodec",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index c52752250565..03752c8405ca 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -472,19 +472,19 @@ static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev)
/* host DSP message data */
static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz);
+ return sof_ops(sdev)->ipc_msg_data(sdev, sps, p, sz);
}
/* host side configuration of the stream's data offset in stream mailbox area */
static inline int
snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
if (sof_ops(sdev) && sof_ops(sdev)->set_stream_data_offset)
- return sof_ops(sdev)->set_stream_data_offset(sdev, substream,
+ return sof_ops(sdev)->set_stream_data_offset(sdev, sps,
posn_offset);
return 0;
@@ -511,6 +511,16 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
return 0;
}
+static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position)
+ return sof_ops(sdev)->get_stream_position(sdev, component, substream);
+
+ return 0;
+}
+
/* machine driver */
static inline int
snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 14571b821eca..445acb5c3a21 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -125,8 +125,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_platform_stream_params platform_params = { 0 };
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sof_pcm *spcm;
int ret;
@@ -143,7 +143,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
*/
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
return ret;
@@ -177,7 +177,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- if (pcm_ops->hw_params) {
+ if (pcm_ops && pcm_ops->hw_params) {
ret = pcm_ops->hw_params(component, substream, params, &platform_params);
if (ret < 0)
return ret;
@@ -196,7 +196,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
int ret, err = 0;
@@ -212,7 +212,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
spcm->pcm.pcm_id, substream->stream);
/* free PCM in the DSP */
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
err = ret;
@@ -279,10 +279,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
bool reset_hw_params = false;
- bool free_widget_list = false;
bool ipc_first = false;
int ret = 0;
@@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
- free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
ipc_first = true;
@@ -344,7 +342,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
if (!ipc_first)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
- if (pcm_ops->trigger)
+ if (pcm_ops && pcm_ops->trigger)
ret = pcm_ops->trigger(component, substream, cmd);
/* need to STOP DMA even if trigger IPC failed */
@@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
if (!ret && reset_hw_params)
- ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
- free_widget_list);
+ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false);
return ret;
}
@@ -569,7 +566,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
struct snd_sof_dai *dai =
snd_sof_find_dai(component, (char *)rtd->dai_link->name);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
/* no topology exists for this BE, try a common configuration */
if (!dai) {
@@ -590,7 +587,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
return 0;
}
- if (pcm_ops->dai_link_fixup)
+ if (pcm_ops && pcm_ops->dai_link_fixup)
return pcm_ops->dai_link_fixup(rtd, params);
return 0;
@@ -649,6 +646,18 @@ static int sof_pcm_ack(struct snd_soc_component *component,
return snd_sof_pcm_platform_ack(sdev, substream);
}
+static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *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);
+
+ if (pcm_ops && pcm_ops->delay)
+ return pcm_ops->delay(component, substream);
+
+ return 0;
+}
+
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
{
struct snd_soc_component_driver *pd = &sdev->plat_drv;
@@ -673,6 +682,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->trigger = sof_pcm_trigger;
pd->pointer = sof_pcm_pointer;
pd->ack = sof_pcm_ack;
+ pd->delay = sof_pcm_delay;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
pd->compress_ops = &sof_compressed_ops;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8722bbd7fd3d..8d3383085d12 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -73,8 +73,8 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
static int sof_resume(struct device *dev, bool runtime_resume)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
u32 old_state = sdev->dsp_power_state.state;
int ret;
@@ -155,7 +155,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
- if (tplg_ops->set_up_all_pipelines) {
+ if (tplg_ops && tplg_ops->set_up_all_pipelines) {
ret = tplg_ops->set_up_all_pipelines(sdev, false);
if (ret < 0) {
dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
@@ -179,8 +179,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
pm_message_t pm_state;
u32 target_state = snd_sof_dsp_power_target(sdev);
int ret;
@@ -276,7 +276,7 @@ suspend:
int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
{
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
/* Notify DSP of upcoming power down */
if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 865c367eb2f2..760621bfc802 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -16,21 +16,23 @@
static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_route *sroute;
list_for_each_entry(sroute, &sdev->route_list, list)
if (sroute->src_widget == widget || sroute->sink_widget == widget) {
- if (sroute->setup && tplg_ops->route_free)
+ if (sroute->setup && tplg_ops && tplg_ops->route_free)
tplg_ops->route_free(sdev, sroute);
sroute->setup = false;
}
}
-int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ struct snd_sof_widget *pipe_widget;
int err = 0;
int ret;
@@ -43,11 +45,13 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
+ pipe_widget = swidget->spipe->pipe_widget;
+
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
/* continue to disable core even if IPC fails */
- if (tplg_ops->widget_free)
+ if (tplg_ops && tplg_ops->widget_free)
err = tplg_ops->widget_free(sdev, swidget);
/*
@@ -67,22 +71,38 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
* skip for static pipelines
*/
if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
- ret = sof_widget_free(sdev, swidget->pipe_widget);
+ ret = sof_widget_free_unlocked(sdev, pipe_widget);
if (ret < 0 && !err)
err = ret;
- swidget->pipe_widget->complete = 0;
}
+ /* clear pipeline complete */
+ if (swidget->id == snd_soc_dapm_scheduler)
+ swidget->spipe->complete = 0;
+
if (!err)
dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
return err;
}
+
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ int ret;
+
+ mutex_lock(&swidget->setup_mutex);
+ ret = sof_widget_free_unlocked(sdev, swidget);
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
+}
EXPORT_SYMBOL(sof_widget_free);
-int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ bool use_count_decremented = false;
int ret;
/* skip if there is no private data */
@@ -103,14 +123,13 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
* widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
*/
if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
- if (!swidget->pipe_widget) {
- dev_err(sdev->dev, "No scheduler widget set for %s\n",
- swidget->widget->name);
+ if (!swidget->spipe || !swidget->spipe->pipe_widget) {
+ dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
ret = -EINVAL;
goto use_count_dec;
}
- ret = sof_widget_setup(sdev, swidget->pipe_widget);
+ ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
if (ret < 0)
goto use_count_dec;
}
@@ -124,7 +143,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
/* setup widget in the DSP */
- if (tplg_ops->widget_setup) {
+ if (tplg_ops && tplg_ops->widget_setup) {
ret = tplg_ops->widget_setup(sdev, swidget);
if (ret < 0)
goto core_put;
@@ -134,7 +153,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (WIDGET_IS_DAI(swidget->id)) {
unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
- if (tplg_ops->dai_config) {
+ if (tplg_ops && tplg_ops->dai_config) {
ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
if (ret < 0)
goto widget_free;
@@ -142,7 +161,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
/* restore kcontrols for widget */
- if (tplg_ops->control->widget_kcontrol_setup) {
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {
ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
if (ret < 0)
goto widget_free;
@@ -154,14 +173,28 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
widget_free:
/* widget use_count and core ref_count will both be decremented by sof_widget_free() */
- sof_widget_free(sdev, swidget);
+ sof_widget_free_unlocked(sdev, swidget);
+ use_count_decremented = true;
core_put:
snd_sof_dsp_core_put(sdev, swidget->core);
pipe_widget_free:
if (swidget->id != snd_soc_dapm_scheduler)
- sof_widget_free(sdev, swidget->pipe_widget);
+ sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
use_count_dec:
- swidget->use_count--;
+ if (!use_count_decremented)
+ swidget->use_count--;
+
+ return ret;
+}
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ int ret;
+
+ mutex_lock(&swidget->setup_mutex);
+ ret = sof_widget_setup_unlocked(sdev, swidget);
+ mutex_unlock(&swidget->setup_mutex);
+
return ret;
}
EXPORT_SYMBOL(sof_widget_setup);
@@ -169,12 +202,11 @@ EXPORT_SYMBOL(sof_widget_setup);
int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
struct snd_soc_dapm_widget *wsink)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *src_widget = wsource->dobj.private;
struct snd_sof_widget *sink_widget = wsink->dobj.private;
struct snd_sof_route *sroute;
bool route_found = false;
- int ret;
/* ignore routes involving virtual widgets in topology */
switch (src_widget->id) {
@@ -212,9 +244,12 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
if (sroute->setup)
return 0;
- ret = ipc_tplg_ops->route_setup(sdev, sroute);
- if (ret < 0)
- return ret;
+ if (tplg_ops && tplg_ops->route_setup) {
+ int ret = tplg_ops->route_setup(sdev, sroute);
+
+ if (ret < 0)
+ return ret;
+ }
sroute->setup = true;
return 0;
@@ -239,24 +274,32 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
if (!widget->dobj.private)
continue;
- snd_soc_dapm_widget_for_each_sink_path(widget, p)
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
+
if (p->sink->dobj.private) {
ret = sof_route_setup(sdev, widget, p->sink);
if (ret < 0)
return ret;
}
+ }
}
} else {
for_each_dapm_widgets(list, i, widget) {
if (!widget->dobj.private)
continue;
- snd_soc_dapm_widget_for_each_source_path(widget, p)
+ snd_soc_dapm_widget_for_each_source_path(widget, p) {
+ if (!widget_in_list(list, p->source))
+ continue;
+
if (p->source->dobj.private) {
ret = sof_route_setup(sdev, p->source, widget);
if (ret < 0)
return ret;
}
+ }
}
}
@@ -264,18 +307,20 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
}
static void
-sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
+sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_widget_list *list)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_dapm_path *p;
/* skip if the widget is in use or if it is already unprepared */
if (!swidget || !swidget->prepared || swidget->use_count > 0)
goto sink_unprepare;
- if (widget_ops[widget->id].ipc_unprepare)
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops && widget_ops[widget->id].ipc_unprepare)
/* unprepare the source widget */
widget_ops[widget->id].ipc_unprepare(swidget);
@@ -284,9 +329,11 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
sink_unprepare:
/* unprepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
- sof_unprepare_widgets_in_path(sdev, p->sink);
+ sof_unprepare_widgets_in_path(sdev, p->sink, list);
p->walking = false;
}
}
@@ -296,14 +343,19 @@ static int
sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
- struct snd_pcm_hw_params *pipeline_params, int dir)
+ struct snd_pcm_hw_params *pipeline_params, int dir,
+ struct snd_soc_dapm_widget_list *list)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_dapm_path *p;
int ret;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (!widget_ops)
+ return 0;
+
if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
goto sink_prepare;
@@ -320,10 +372,13 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
sink_prepare:
/* prepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
- platform_params, pipeline_params, dir);
+ platform_params, pipeline_params, dir,
+ list);
p->walking = false;
if (ret < 0) {
/* unprepare the source widget */
@@ -345,27 +400,28 @@ sink_prepare:
* (DAI type for capture, AIF type for playback)
*/
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- int dir)
+ int dir, struct snd_sof_pcm *spcm)
{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
- /* free all widgets even in case of error to keep use counts balanced */
+ if (widget->dobj.private) {
+ err = sof_widget_free(sdev, widget->dobj.private);
+ if (err < 0)
+ ret = err;
+ }
+
+ /* free all widgets in the sink paths even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
- if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
- p->walking = true;
- if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
- err = sof_widget_free(sdev, widget->dobj.private);
- if (err < 0)
- ret = err;
- }
+ if (!p->walking) {
+ if (!widget_in_list(list, p->sink))
+ continue;
- err = sof_widget_free(sdev, p->sink->dobj.private);
- if (err < 0)
- ret = err;
+ p->walking = true;
- err = sof_free_widgets_in_path(sdev, p->sink, dir);
+ err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
if (err < 0)
ret = err;
p->walking = false;
@@ -381,37 +437,58 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
* The error path in this function ensures that all successfully set up widgets getting freed.
*/
static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- int dir)
+ int dir, struct snd_sof_pcm *spcm)
{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_pipeline *spipe;
struct snd_soc_dapm_path *p;
int ret;
+ if (swidget) {
+ int i;
+
+ ret = sof_widget_setup(sdev, widget->dobj.private);
+ if (ret < 0)
+ return ret;
+
+ /* skip populating the pipe_widgets array if it is NULL */
+ if (!pipeline_list->pipelines)
+ goto sink_setup;
+
+ /*
+ * Add the widget's pipe_widget to the list of pipelines to be triggered if not
+ * already in the list. This will result in the pipelines getting added in the
+ * order source to sink.
+ */
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ if (spipe == swidget->spipe)
+ break;
+ }
+
+ if (i == pipeline_list->count) {
+ pipeline_list->count++;
+ pipeline_list->pipelines[i] = swidget->spipe;
+ }
+ }
+
+sink_setup:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
- if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
- p->walking = true;
- if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
- ret = sof_widget_setup(sdev, widget->dobj.private);
- if (ret < 0)
- goto out;
- }
+ if (!p->walking) {
+ if (!widget_in_list(list, p->sink))
+ continue;
- ret = sof_widget_setup(sdev, p->sink->dobj.private);
- if (ret < 0) {
- if (WIDGET_IS_AIF_OR_DAI(widget->id))
- sof_widget_free(sdev, widget->dobj.private);
- goto out;
- }
+ p->walking = true;
- ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
- if (ret < 0) {
- if (WIDGET_IS_AIF_OR_DAI(widget->id))
- sof_widget_free(sdev, widget->dobj.private);
- sof_widget_free(sdev, p->sink->dobj.private);
- }
-out:
+ ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
p->walking = false;
- if (ret < 0)
+ if (ret < 0) {
+ if (swidget)
+ sof_widget_free(sdev, swidget);
return ret;
+ }
}
}
@@ -419,16 +496,20 @@ out:
}
static int
-sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
+sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params, int dir,
enum sof_widget_op op)
{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
char *str;
int ret = 0;
int i;
+ if (!list)
+ return 0;
+
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
@@ -440,11 +521,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
switch (op) {
case SOF_WIDGET_SETUP:
- ret = sof_set_up_widgets_in_path(sdev, widget, dir);
+ ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
str = "set up";
break;
case SOF_WIDGET_FREE:
- ret = sof_free_widgets_in_path(sdev, widget, dir);
+ ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
str = "free";
break;
case SOF_WIDGET_PREPARE:
@@ -460,12 +541,12 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
*/
memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
- ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
- platform_params, &pipeline_params, dir);
+ ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
+ &pipeline_params, dir, list);
break;
}
case SOF_WIDGET_UNPREPARE:
- sof_unprepare_widgets_in_path(sdev, widget);
+ sof_unprepare_widgets_in_path(sdev, widget, list);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
@@ -485,7 +566,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_sof_platform_stream_params *platform_params,
int dir)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret;
@@ -498,16 +579,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
* Prepare widgets for set up. The prepare step is used to allocate memory, assign
* instance ID and pick the widget configuration based on the runtime PCM params.
*/
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_PREPARE);
if (ret < 0)
return ret;
/* Set up is used to send the IPC to the DSP to create the widget */
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
if (ret < 0) {
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_UNPREPARE);
return ret;
}
@@ -524,11 +605,20 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_sof_widget *pipe_widget;
+ struct snd_sof_pipeline *spipe;
if (!swidget)
continue;
- pipe_widget = swidget->pipe_widget;
+ spipe = swidget->spipe;
+ if (!spipe) {
+ dev_err(sdev->dev, "no pipeline found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ pipe_widget = spipe->pipe_widget;
if (!pipe_widget) {
dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
swidget->widget->name);
@@ -536,13 +626,13 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
goto widget_free;
}
- if (pipe_widget->complete)
+ if (spipe->complete)
continue;
- if (ipc_tplg_ops->pipeline_complete) {
- pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget);
- if (pipe_widget->complete < 0) {
- ret = pipe_widget->complete;
+ if (tplg_ops && tplg_ops->pipeline_complete) {
+ spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
+ if (spipe->complete < 0) {
+ ret = spipe->complete;
goto widget_free;
}
}
@@ -551,15 +641,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
widget_free:
- sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
+ sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
SOF_WIDGET_FREE);
- sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
return ret;
}
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
int ret;
@@ -568,14 +659,16 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
return 0;
/* send IPC to free widget in the DSP */
- ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
+ ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
/* unprepare the widget */
- sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
+ pipeline_list->count = 0;
+
return ret;
}
@@ -628,11 +721,11 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
{
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
/* Send PCM_FREE IPC to reset pipeline */
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
@@ -762,13 +855,13 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
struct snd_sof_dai *dai =
snd_sof_find_dai(component, (char *)rtd->dai_link->name);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
/* use the tplg configured mclk if existed */
if (!dai)
return 0;
- if (tplg_ops->dai_get_clk)
+ if (tplg_ops && tplg_ops->dai_get_clk)
return tplg_ops->dai_get_clk(sdev, dai, clk_type);
return 0;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 29cf951e3526..e0579af9d281 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -42,6 +42,7 @@
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
+#define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer)
#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
@@ -85,6 +86,7 @@ struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_control;
struct snd_sof_dai;
+struct snd_sof_pcm;
struct snd_sof_dai_config_data {
int dai_index;
@@ -97,6 +99,11 @@ struct snd_sof_dai_config_data {
* @hw_free: Function pointer for hw_free
* @trigger: Function pointer for trigger
* @dai_link_fixup: Function pointer for DAI link fixup
+ * @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating
+ * additional memory in the SOF PCM stream structure
+ * @pcm_free: Function pointer for PCM free that can be used for freeing any
+ * additional memory in the SOF PCM stream structure
+ * @delay: Function pointer for pcm delay calculation
*/
struct sof_ipc_pcm_ops {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -106,6 +113,10 @@ struct sof_ipc_pcm_ops {
int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
int cmd);
int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
+ int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
+ void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
+ snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
};
/**
@@ -166,7 +177,7 @@ struct sof_ipc_tplg_widget_ops {
* initialized to 0.
* @control: Pointer to the IPC-specific ops for topology kcontrol IO
* @route_setup: Function pointer for setting up pipeline connections
- * @route_free: Optional op for freeing pipeline connections.
+ * @route_free: Function pointer for freeing pipeline connections.
* @token_list: List of all tokens supported by the IPC version. The size of the token_list
* array should be SOF_TOKEN_COUNT. The unused elements in the array will be
* initialized to 0.
@@ -179,7 +190,10 @@ struct sof_ipc_tplg_widget_ops {
* @dai_get_clk: Function pointer for getting the DAI clock setting
* @set_up_all_pipelines: Function pointer for setting up all topology pipelines
* @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
- * @parse_manifest: Optional function pointer for ipc4 specific parsing of topology manifest
+ * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest
+ * @link_setup: Function pointer for IPC-specific DAI link set up
+ *
+ * Note: function pointers (ops) are optional
*/
struct sof_ipc_tplg_ops {
const struct sof_ipc_tplg_widget_ops *widget;
@@ -199,6 +213,7 @@ struct sof_ipc_tplg_ops {
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*parse_manifest)(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man);
+ int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link);
};
/** struct snd_sof_tuple - Tuple info
@@ -274,6 +289,16 @@ struct sof_token_info {
int count;
};
+/**
+ * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream
+ * @count: number of pipeline widgets in the @pipe_widgets array
+ * @pipelines: array of pipelines
+ */
+struct snd_sof_pcm_stream_pipeline_list {
+ u32 count;
+ struct snd_sof_pipeline **pipelines;
+};
+
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
@@ -289,6 +314,10 @@ struct snd_sof_pcm_stream {
* active or not while suspending the stream
*/
bool suspend_ignored;
+ struct snd_sof_pcm_stream_pipeline_list pipeline_list;
+
+ /* used by IPC implementation and core does not touch it */
+ void *private;
};
/* ALSA SOF PCM device */
@@ -361,16 +390,20 @@ struct snd_sof_widget {
int comp_id;
int pipeline_id;
/*
- * complete flag is used to indicate that pipeline set up is complete for scheduler type
- * widgets. It is unused for all other widget types.
- */
- int complete;
- /*
* the prepared flag is used to indicate that a widget has been prepared for getting set
* up in the DSP.
*/
bool prepared;
- int use_count; /* use_count will be protected by the PCM mutex held by the core */
+
+ struct mutex setup_mutex; /* to protect the swidget setup and free operations */
+
+ /*
+ * use_count is protected by the PCM mutex held by the core and the
+ * setup_mutex against non stream domain races (kcontrol access for
+ * example)
+ */
+ int use_count;
+
int core;
int id; /* id is the DAPM widget type */
/*
@@ -391,7 +424,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
- struct snd_sof_widget *pipe_widget;
+ struct snd_sof_pipeline *spipe;
void *module_info;
const guid_t uuid;
@@ -429,6 +462,22 @@ struct snd_sof_widget {
void *private; /* core does not touch this */
};
+/** struct snd_sof_pipeline - ASoC SOF pipeline
+ * @pipe_widget: Pointer to the pipeline widget
+ * @started_count: Count of number of PCM's that have started this pipeline
+ * @paused_count: Count of number of PCM's that have started and have currently paused this
+ pipeline
+ * @complete: flag used to indicate that pipeline set up is complete.
+ * @list: List item in sdev pipeline_list
+ */
+struct snd_sof_pipeline {
+ struct snd_sof_widget *pipe_widget;
+ int started_count;
+ int paused_count;
+ int complete;
+ struct list_head list;
+};
+
/* ASoC SOF DAPM route */
struct snd_sof_route {
struct snd_soc_component *scomp;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 6760fd895950..5f919162a555 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -20,6 +20,8 @@
#include <uapi/sound/sof/fw.h>
#include <sound/sof/ext_manifest.h>
+struct snd_sof_pcm_stream;
+
/* Flag definitions used in sof_core_debug (sof_debug module parameter) */
#define SOF_DBG_ENABLE_TRACE BIT(0)
#define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */
@@ -113,6 +115,7 @@ struct sof_compr_stream {
u32 sampling_rate;
u16 channels;
u16 sample_container_bytes;
+ size_t posn_offset;
};
struct snd_sof_dev;
@@ -245,14 +248,23 @@ struct snd_sof_dsp_ops {
/* pcm ack */
int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
+ /*
+ * optional callback to retrieve the link DMA position for the substream
+ * when the position is not reported in the shared SRAM windows but
+ * instead from a host-accessible hardware counter.
+ */
+ u64 (*get_stream_position)(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream); /* optional */
+
/* host read DSP stream data */
int (*ipc_msg_data)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz); /* mandatory */
/* host side configuration of the stream's data offset in stream mailbox area */
int (*set_stream_data_offset)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset); /* optional */
/* pre/post firmware run */
@@ -413,11 +425,13 @@ struct sof_ipc_fw_tracing_ops {
* @ctx_save: Optional function pointer for context save
* @ctx_restore: Optional function pointer for context restore
* @set_core_state: Optional function pointer for turning on/off a DSP core
+ * @set_pm_gate: Optional function pointer for pm gate settings
*/
struct sof_ipc_pm_ops {
int (*ctx_save)(struct snd_sof_dev *sdev);
int (*ctx_restore)(struct snd_sof_dev *sdev);
int (*set_core_state)(struct snd_sof_dev *sdev, int core_idx, bool on);
+ int (*set_pm_gate)(struct snd_sof_dev *sdev, u32 flags);
};
/**
@@ -444,7 +458,7 @@ struct sof_ipc_pcm_ops;
* @pm: Pointer to PM ops
* @pcm: Pointer to PCM ops
* @fw_loader: Pointer to Firmware Loader ops
- * @fw_tracing: Pointer to Firmware tracing ops
+ * @fw_tracing: Optional pointer to Firmware tracing ops
*
* @init: Optional pointer for IPC related initialization
* @exit: Optional pointer for IPC related cleanup
@@ -502,6 +516,10 @@ struct snd_sof_ipc {
const struct sof_ipc_ops *ops;
};
+/* Helper to retrieve the IPC ops */
+#define sof_ipc_get_ops(sdev, ops_name) \
+ (((sdev)->ipc && (sdev)->ipc->ops) ? (sdev)->ipc->ops->ops_name : NULL)
+
/*
* SOF Device Level.
*/
@@ -541,6 +559,7 @@ struct snd_sof_dev {
/* IPC */
struct snd_sof_ipc *ipc;
+ struct snd_sof_mailbox fw_info_box; /* FW shared memory */
struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */
struct snd_sof_mailbox host_box; /* Host initiated IPC */
struct snd_sof_mailbox stream_box; /* Stream position update */
@@ -571,6 +590,7 @@ struct snd_sof_dev {
struct list_head pcm_list;
struct list_head kcontrol_list;
struct list_head widget_list;
+ struct list_head pipeline_list;
struct list_head dai_list;
struct list_head dai_link_list;
struct list_head route_list;
@@ -757,10 +777,10 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
u32 offset, void *dest, size_t size);
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
int sof_stream_pcm_open(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c
index 5f1ceeea893a..216b454f6b94 100644
--- a/sound/soc/sof/stream-ipc.c
+++ b/sound/soc/sof/stream-ipc.c
@@ -19,6 +19,7 @@
#include "ops.h"
#include "sof-priv.h"
+#include "sof-audio.h"
struct sof_stream {
size_t posn_offset;
@@ -26,19 +27,33 @@ struct sof_stream {
/* Mailbox-based Generic IPC implementation */
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
- struct sof_stream *stream = substream->runtime->private_data;
+ size_t posn_offset;
- /* The stream might already be closed */
- if (!stream)
- return -ESTRPIPE;
+ if (sps->substream) {
+ struct sof_stream *stream = sps->substream->runtime->private_data;
- snd_sof_dsp_mailbox_read(sdev, stream->posn_offset, p, sz);
+ /* The stream might already be closed */
+ if (!stream)
+ return -ESTRPIPE;
+
+ posn_offset = stream->posn_offset;
+ } else {
+
+ struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
+
+ if (!sstream)
+ return -ESTRPIPE;
+
+ posn_offset = sstream->posn_offset;
+ }
+
+ snd_sof_dsp_mailbox_read(sdev, posn_offset, p, sz);
}
return 0;
@@ -46,20 +61,32 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev,
EXPORT_SYMBOL(sof_ipc_msg_data);
int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
- struct sof_stream *stream = substream->runtime->private_data;
-
/* check if offset is overflow or it is not aligned */
if (posn_offset > sdev->stream_box.size ||
posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
return -EINVAL;
- stream->posn_offset = sdev->stream_box.offset + posn_offset;
+ posn_offset += sdev->stream_box.offset;
+
+ if (sps->substream) {
+ struct sof_stream *stream = sps->substream->runtime->private_data;
+
+ stream->posn_offset = posn_offset;
+ dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
+ sps->substream->stream, posn_offset);
+ } else if (sps->cstream) {
+ struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
- dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
- substream->stream, stream->posn_offset);
+ sstream->posn_offset = posn_offset;
+ dev_dbg(sdev->dev, "compr: stream dir %d, posn mailbox offset is %zu",
+ sps->cstream->direction, posn_offset);
+ } else {
+ dev_err(sdev->dev, "No stream opened");
+ return -EINVAL;
+ }
return 0;
}
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index c668bd9d21ec..4a62ccc71fcb 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -54,11 +54,16 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
size_t object_size, int token_instance_num)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_token_info *token_list;
const struct sof_topology_token *tokens;
int i, j;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
+ /* nothing to do if token_list is NULL */
+ if (!token_list)
+ return 0;
+
if (token_list[token_id].count < 0) {
dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id);
return -EINVAL;
@@ -263,9 +268,9 @@ static int set_up_volume_table(struct snd_sof_control *scontrol,
{
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->set_up_volume_table)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table)
return tplg_ops->control->set_up_volume_table(scontrol, tlv, size);
dev_err(scomp->dev, "Mandatory op %s not set\n", __func__);
@@ -490,13 +495,14 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_
int array_size, u32 token_id, int token_instance_num,
struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_token_info *token_list;
const struct sof_topology_token *tokens;
int found = 0;
int num_tokens, asize;
int i, j;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
/* nothing to do if token_list is NULL */
if (!token_list)
return 0;
@@ -1015,14 +1021,14 @@ static int sof_control_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_control *scontrol = dobj->private;
int ret = 0;
dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name);
- if (ipc_tplg_ops->control_free) {
- ret = ipc_tplg_ops->control_free(sdev, scontrol);
+ if (tplg_ops && tplg_ops->control_free) {
+ ret = tplg_ops->control_free(sdev, scontrol);
if (ret < 0)
dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name);
}
@@ -1049,6 +1055,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_card *card = scomp->card;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *cpu_dai;
+ int stream;
int i;
if (!w->sname) {
@@ -1056,62 +1063,41 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
return -EINVAL;
}
+ if (w->id == snd_soc_dapm_dai_out)
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+ else if (w->id == snd_soc_dapm_dai_in)
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ goto end;
+
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
strcmp(w->sname, rtd->dai_link->stream_name))
continue;
- switch (w->id) {
- case snd_soc_dapm_dai_out:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- /*
- * Please create DAI widget in the right order
- * to ensure BE will connect to the right DAI
- * widget.
- */
- if (!cpu_dai->capture_widget) {
- cpu_dai->capture_widget = w;
- break;
- }
- }
- if (i == rtd->dai_link->num_cpus) {
- dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
- w->name);
-
- return -EINVAL;
- }
- dai->name = rtd->dai_link->name;
- dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
- w->name, rtd->dai_link->name);
- break;
- case snd_soc_dapm_dai_in:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- /*
- * Please create DAI widget in the right order
- * to ensure BE will connect to the right DAI
- * widget.
- */
- if (!cpu_dai->playback_widget) {
- cpu_dai->playback_widget = w;
- break;
- }
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ /*
+ * Please create DAI widget in the right order
+ * to ensure BE will connect to the right DAI
+ * widget.
+ */
+ if (!snd_soc_dai_get_widget(cpu_dai, stream)) {
+ snd_soc_dai_set_widget(cpu_dai, stream, w);
+ break;
}
- if (i == rtd->dai_link->num_cpus) {
- dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
- w->name);
+ }
+ if (i == rtd->dai_link->num_cpus) {
+ dev_err(scomp->dev, "error: can't find BE for DAI %s\n", w->name);
- return -EINVAL;
- }
- dai->name = rtd->dai_link->name;
- dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
- w->name, rtd->dai_link->name);
- break;
- default:
- break;
+ return -EINVAL;
}
- }
+ dai->name = rtd->dai_link->name;
+ dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
+ w->name, rtd->dai_link->name);
+ }
+end:
/* check we have a connection */
if (!dai->name) {
dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n",
@@ -1128,37 +1114,29 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_card *card = scomp->card;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *cpu_dai;
- int i;
+ int i, stream;
if (!w->sname)
return;
+ if (w->id == snd_soc_dapm_dai_out)
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+ else if (w->id == snd_soc_dapm_dai_in)
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ return;
+
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
strcmp(w->sname, rtd->dai_link->stream_name))
continue;
- switch (w->id) {
- case snd_soc_dapm_dai_out:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- if (cpu_dai->capture_widget == w) {
- cpu_dai->capture_widget = NULL;
- break;
- }
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ if (snd_soc_dai_get_widget(cpu_dai, stream) == w) {
+ snd_soc_dai_set_widget(cpu_dai, stream, NULL);
+ break;
}
- break;
- case snd_soc_dapm_dai_in:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- if (cpu_dai->playback_widget == w) {
- cpu_dai->playback_widget = NULL;
- break;
- }
- }
- break;
- default:
- break;
- }
}
}
@@ -1201,12 +1179,17 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
enum sof_tokens *object_token_list, int count)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_tplg_private *private = &tw->priv;
+ const struct sof_token_info *token_list;
int num_tuples = 0;
int ret, i;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
+ /* nothing to do if token_list is NULL */
+ if (!token_list)
+ return 0;
+
if (count > 0 && !object_token_list) {
dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name);
return -EINVAL;
@@ -1375,13 +1358,13 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_dapm_widget *tw)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_tplg_private *priv = &tw->priv;
+ enum sof_tokens *token_list = NULL;
struct snd_sof_widget *swidget;
struct snd_sof_dai *dai;
- enum sof_tokens *token_list;
- int token_list_size;
+ int token_list_size = 0;
int ret = 0;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
@@ -1391,10 +1374,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->scomp = scomp;
swidget->widget = w;
swidget->comp_id = sdev->next_comp_id++;
- swidget->complete = 0;
swidget->id = w->id;
swidget->pipeline_id = index;
swidget->private = NULL;
+ mutex_init(&swidget->setup_mutex);
+
ida_init(&swidget->src_queue_ida);
ida_init(&swidget->sink_queue_ida);
@@ -1440,8 +1424,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->num_sink_pins, swidget->num_source_pins,
strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none");
- token_list = widget_ops[w->id].token_list;
- token_list_size = widget_ops[w->id].token_list_size;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops) {
+ token_list = widget_ops[w->id].token_list;
+ token_list_size = widget_ops[w->id].token_list_size;
+ }
/* handle any special case widgets */
switch (w->id) {
@@ -1525,7 +1512,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
/* bind widget to external event */
if (tw->event_type) {
- if (widget_ops[w->id].bind_event) {
+ if (widget_ops && widget_ops[w->id].bind_event) {
ret = widget_ops[w->id].bind_event(scomp, swidget,
le16_to_cpu(tw->event_type));
if (ret) {
@@ -1539,6 +1526,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
}
}
+ /* create and add pipeline for scheduler type widgets */
+ if (w->id == snd_soc_dapm_scheduler) {
+ struct snd_sof_pipeline *spipe;
+
+ spipe = kzalloc(sizeof(*spipe), GFP_KERNEL);
+ if (!spipe) {
+ kfree(swidget->private);
+ kfree(swidget->tuples);
+ kfree(swidget);
+ return -ENOMEM;
+ }
+
+ spipe->pipe_widget = swidget;
+ swidget->spipe = spipe;
+ list_add(&spipe->list, &sdev->pipeline_list);
+ }
+
w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list);
return ret;
@@ -1565,8 +1569,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
const struct snd_kcontrol_new *kc;
struct snd_soc_dapm_widget *widget;
struct snd_sof_control *scontrol;
@@ -1594,6 +1598,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
sof_disconnect_dai_widget(scomp, widget);
break;
+ case snd_soc_dapm_scheduler:
+ {
+ struct snd_sof_pipeline *spipe = swidget->spipe;
+
+ list_del(&spipe->list);
+ kfree(spipe);
+ swidget->spipe = NULL;
+ break;
+ }
default:
break;
}
@@ -1626,7 +1639,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
out:
/* free IPC related data */
- if (widget_ops[swidget->id].ipc_free)
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops && widget_ops[swidget->id].ipc_free)
widget_ops[swidget->id].ipc_free(swidget);
ida_destroy(&swidget->src_queue_ida);
@@ -1654,6 +1668,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_soc_tplg_stream_caps *caps;
struct snd_soc_tplg_private *private = &pcm->priv;
struct snd_sof_pcm *spcm;
@@ -1681,6 +1696,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
spcm->pcm = *pcm;
dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name);
+ /* perform pcm set op */
+ if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) {
+ ret = ipc_pcm_ops->pcm_setup(sdev, spcm);
+ if (ret < 0)
+ return ret;
+ }
+
dai_drv->dobj.private = spcm;
list_add(&spcm->list, &sdev->pcm_list);
@@ -1758,6 +1780,8 @@ free_playback_tables:
static int sof_dai_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm = dobj->private;
/* free PCM DMA pages */
@@ -1767,6 +1791,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
if (spcm->pcm.capture)
snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table);
+ /* perform pcm free op */
+ if (ipc_pcm_ops && ipc_pcm_ops->pcm_free)
+ ipc_pcm_ops->pcm_free(sdev, spcm);
+
/* remove from list and free spcm */
list_del(&spcm->list);
kfree(spcm);
@@ -1784,9 +1812,9 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
struct snd_soc_tplg_link_config *cfg)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_tplg_private *private = &cfg->priv;
+ const struct sof_token_info *token_list;
struct snd_sof_dai_link *slink;
u32 token_id = 0;
int num_tuples = 0;
@@ -1798,26 +1826,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
}
link->platforms->name = dev_name(scomp->dev);
- /*
- * Set nonatomic property for FE dai links as their trigger action
- * involves IPC's.
- */
+ if (tplg_ops && tplg_ops->link_setup) {
+ ret = tplg_ops->link_setup(sdev, link);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set nonatomic property for FE dai links as their trigger action involves IPC's */
if (!link->no_pcm) {
link->nonatomic = true;
-
- /*
- * set default trigger order for all links. Exceptions to
- * the rule will be handled in sof_pcm_dai_link_fixup()
- * For playback, the sequence is the following: start FE,
- * start BE, stop BE, stop FE; for Capture the sequence is
- * inverted start BE, start FE, stop FE, stop BE
- */
- link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
- SND_SOC_DPCM_TRIGGER_PRE;
- link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
- SND_SOC_DPCM_TRIGGER_POST;
-
- /* nothing more to do for FE dai links */
return 0;
}
@@ -1856,6 +1873,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
return ret;
}
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
if (!token_list)
goto out;
@@ -2063,18 +2081,19 @@ err:
}
/**
- * sof_set_pipe_widget - Set pipe_widget for a component
+ * sof_set_widget_pipeline - Set pipeline for a component
* @sdev: pointer to struct snd_sof_dev
- * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @spipe: pointer to struct snd_sof_pipeline
* @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
*
* Return: 0 if successful, -EINVAL on error.
* The function checks if @swidget is associated with any volatile controls. If so, setting
* the dynamic_pipeline_widget is disallowed.
*/
-static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
- struct snd_sof_widget *swidget)
+static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe,
+ struct snd_sof_widget *swidget)
{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct snd_sof_control *scontrol;
if (pipe_widget->dynamic_pipeline_widget) {
@@ -2089,8 +2108,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
}
}
- /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
- swidget->pipe_widget = pipe_widget;
+ /* set the pipeline and apply the dynamic_pipeline_widget_flag */
+ swidget->spipe = spipe;
swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
return 0;
@@ -2100,16 +2119,19 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget, *comp_swidget;
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_sof_control *scontrol;
+ struct snd_sof_pipeline *spipe;
int ret;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+
/* first update all control IPC structures based on the IPC version */
- if (ipc_tplg_ops->control_setup)
+ if (tplg_ops && tplg_ops->control_setup)
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- ret = ipc_tplg_ops->control_setup(sdev, scontrol);
+ ret = tplg_ops->control_setup(sdev, scontrol);
if (ret < 0) {
dev_err(sdev->dev, "failed updating IPC struct for control %s\n",
scontrol->name);
@@ -2123,7 +2145,7 @@ static int sof_complete(struct snd_soc_component *scomp)
* associated memories.
*/
list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (widget_ops[swidget->id].ipc_setup) {
+ if (widget_ops && widget_ops[swidget->id].ipc_setup) {
ret = widget_ops[swidget->id].ipc_setup(swidget);
if (ret < 0) {
dev_err(sdev->dev, "failed updating IPC struct for %s\n",
@@ -2134,36 +2156,35 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- switch (swidget->id) {
- case snd_soc_dapm_scheduler:
- /*
- * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
- * for all widgets that have the same pipeline ID as the scheduler widget
- */
- list_for_each_entry(comp_swidget, &sdev->widget_list, list)
- if (comp_swidget->pipeline_id == swidget->pipeline_id) {
- ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
- if (ret < 0)
- return ret;
- }
- break;
- default:
- break;
- }
+ list_for_each_entry(spipe, &sdev->pipeline_list, list) {
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+
+ /*
+ * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+ * for all widgets that have the same pipeline ID as the scheduler widget.
+ * Skip the scheduler widgets as they have their pipeline set during widget_ready
+ */
+ list_for_each_entry(comp_swidget, &sdev->widget_list, list)
+ if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
+ comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
+ ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
+ if (ret < 0)
+ return ret;
+ }
}
/* verify topology components loading including dynamic pipelines */
if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) {
- if (ipc_tplg_ops->set_up_all_pipelines && ipc_tplg_ops->tear_down_all_pipelines) {
- ret = ipc_tplg_ops->set_up_all_pipelines(sdev, true);
+ if (tplg_ops && tplg_ops->set_up_all_pipelines &&
+ tplg_ops->tear_down_all_pipelines) {
+ ret = tplg_ops->set_up_all_pipelines(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n",
ret);
return ret;
}
- ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true);
+ ret = tplg_ops->tear_down_all_pipelines(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n",
ret);
@@ -2173,8 +2194,8 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* set up static pipelines */
- if (ipc_tplg_ops->set_up_all_pipelines)
- return ipc_tplg_ops->set_up_all_pipelines(sdev, false);
+ if (tplg_ops && tplg_ops->set_up_all_pipelines)
+ return tplg_ops->set_up_all_pipelines(sdev, false);
return 0;
}
@@ -2184,10 +2205,10 @@ static int sof_manifest(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (ipc_tplg_ops->parse_manifest)
- return ipc_tplg_ops->parse_manifest(scomp, index, man);
+ if (tplg_ops && tplg_ops->parse_manifest)
+ return tplg_ops->parse_manifest(scomp, index, man);
return 0;
}
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index 6f662642d611..b2ab51e5214a 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -6,19 +6,21 @@
int sof_fw_trace_init(struct snd_sof_dev *sdev)
{
- if (!sdev->ipc->ops->fw_tracing) {
+ const struct sof_ipc_fw_tracing_ops *fw_tracing = sof_ipc_get_ops(sdev, fw_tracing);
+
+ if (!fw_tracing) {
dev_info(sdev->dev, "Firmware tracing is not available\n");
sdev->fw_trace_is_supported = false;
return 0;
}
- return sdev->ipc->ops->fw_tracing->init(sdev);
+ return fw_tracing->init(sdev);
}
void sof_fw_trace_free(struct snd_sof_dev *sdev)
{
- if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing)
+ if (!sdev->fw_trace_is_supported)
return;
if (sdev->ipc->ops->fw_tracing->free)