summaryrefslogtreecommitdiff
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/Kconfig33
-rw-r--r--sound/soc/sof/Makefile18
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h1
-rw-r--r--sound/soc/sof/amd/acp-ipc.c22
-rw-r--r--sound/soc/sof/amd/acp-loader.c9
-rw-r--r--sound/soc/sof/amd/acp-pcm.c15
-rw-r--r--sound/soc/sof/amd/acp-stream.c3
-rw-r--r--sound/soc/sof/amd/acp-trace.c38
-rw-r--r--sound/soc/sof/amd/acp.c29
-rw-r--r--sound/soc/sof/amd/acp.h9
-rw-r--r--sound/soc/sof/amd/renoir.c1
-rw-r--r--sound/soc/sof/compress.c274
-rw-r--r--sound/soc/sof/control.c545
-rw-r--r--sound/soc/sof/core.c73
-rw-r--r--sound/soc/sof/debug.c567
-rw-r--r--sound/soc/sof/imx/imx8.c4
-rw-r--r--sound/soc/sof/imx/imx8m.c3
-rw-r--r--sound/soc/sof/intel/Kconfig19
-rw-r--r--sound/soc/sof/intel/apl.c15
-rw-r--r--sound/soc/sof/intel/bdw.c2
-rw-r--r--sound/soc/sof/intel/byt.c4
-rw-r--r--sound/soc/sof/intel/cnl.c21
-rw-r--r--sound/soc/sof/intel/hda-dai.c204
-rw-r--r--sound/soc/sof/intel/hda-dsp.c20
-rw-r--r--sound/soc/sof/intel/hda-ipc.c18
-rw-r--r--sound/soc/sof/intel/hda-loader.c118
-rw-r--r--sound/soc/sof/intel/hda-pcm.c31
-rw-r--r--sound/soc/sof/intel/hda-probes.c104
-rw-r--r--sound/soc/sof/intel/hda-stream.c122
-rw-r--r--sound/soc/sof/intel/hda-trace.c23
-rw-r--r--sound/soc/sof/intel/hda.c307
-rw-r--r--sound/soc/sof/intel/hda.h90
-rw-r--r--sound/soc/sof/intel/icl.c15
-rw-r--r--sound/soc/sof/intel/pci-tgl.c2
-rw-r--r--sound/soc/sof/intel/pci-tng.c3
-rw-r--r--sound/soc/sof/intel/tgl.c15
-rw-r--r--sound/soc/sof/iomem-utils.c (renamed from sound/soc/sof/utils.c)61
-rw-r--r--sound/soc/sof/ipc.c142
-rw-r--r--sound/soc/sof/ipc3-control.c594
-rw-r--r--sound/soc/sof/ipc3-ops.h21
-rw-r--r--sound/soc/sof/ipc3-pcm.c372
-rw-r--r--sound/soc/sof/ipc3-topology.c2393
-rw-r--r--sound/soc/sof/ipc3.c45
-rw-r--r--sound/soc/sof/ops.h68
-rw-r--r--sound/soc/sof/pcm.c353
-rw-r--r--sound/soc/sof/pm.c93
-rw-r--r--sound/soc/sof/sof-audio.c546
-rw-r--r--sound/soc/sof/sof-audio.h235
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c396
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c192
-rw-r--r--sound/soc/sof/sof-client-probes.c821
-rw-r--r--sound/soc/sof/sof-client-probes.h31
-rw-r--r--sound/soc/sof/sof-client.c469
-rw-r--r--sound/soc/sof/sof-client.h67
-rw-r--r--sound/soc/sof/sof-pci-dev.c16
-rw-r--r--sound/soc/sof/sof-priv.h221
-rw-r--r--sound/soc/sof/sof-probes.c364
-rw-r--r--sound/soc/sof/sof-probes.h38
-rw-r--r--sound/soc/sof/sof-utils.c77
-rw-r--r--sound/soc/sof/sof-utils.h19
-rw-r--r--sound/soc/sof/stream-ipc.c9
-rw-r--r--sound/soc/sof/topology.c2888
-rw-r--r--sound/soc/sof/trace.c21
63 files changed, 7886 insertions, 5443 deletions
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index 1a7d6cefd3b7..4542868cd730 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -53,13 +53,21 @@ config SND_SOC_SOF_COMPRESS
select SND_SOC_COMPRESS
config SND_SOC_SOF_DEBUG_PROBES
- bool "SOF enable data probing"
+ tristate
+ select SND_SOC_SOF_CLIENT
select SND_SOC_COMPRESS
help
This option enables the data probing feature that can be used to
gather data directly from specific points of the audio pipeline.
- Say Y if you want to enable probes.
- If unsure, select "N".
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_CLIENT
+ tristate
+ select AUXILIARY_BUS
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
config SND_SOC_SOF_DEVELOPER_SUPPORT
bool "SOF developer options support"
@@ -187,15 +195,26 @@ config SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE
If unsure, select "N".
config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
- bool "SOF enable IPC flood test"
+ tristate "SOF enable IPC flood test"
+ select SND_SOC_SOF_CLIENT
help
- This option enables the IPC flood test which can be used to flood
- the DSP with test IPCs and gather stats about response times.
+ This option enables a separate client device for IPC flood test
+ which can be used to flood the DSP with test IPCs and gather stats
+ about response times.
Say Y if you want to enable IPC flood test.
If unsure, select "N".
+config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM
+ int "Number of IPC flood test clients"
+ range 1 32
+ default 2
+ depends on SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
+ help
+ Select the number of IPC flood test clients to be created.
+
config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR
- bool "SOF enable IPC message injector"
+ tristate "SOF enable IPC message injector"
+ select SND_SOC_SOF_CLIENT
help
This option enables the IPC message injector which can be used to send
crafted IPC messages to the DSP to test its robustness.
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 964b429146be..18acbc001b9a 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -1,25 +1,39 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
- control.o trace.o utils.o sof-audio.o stream-ipc.o
+ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
+ ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o
+ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),)
+snd-sof-objs += sof-client.o
+endif
-snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o
snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o
snd-sof-pci-objs := sof-pci-dev.o
snd-sof-acpi-objs := sof-acpi-dev.o
snd-sof-of-objs := sof-of-dev.o
+snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o
+snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o
+snd-sof-probes-objs := sof-client-probes.o
+
snd-sof-nocodec-objs := nocodec.o
+snd-sof-utils-objs := sof-utils.o
+
obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o
obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o
+obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o
obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o
obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o
obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o
+obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o
+obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o
+obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o
+
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index 63f13c111b24..40fbf11facba 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -61,6 +61,7 @@
#define ACP_DSP_SW_INTR_STAT 0x1818
#define ACP_SW_INTR_TRIG 0x181C
#define ACP_ERROR_STATUS 0x18C4
+#define ACP_AXI2DAGB_SEM_0 0x1880
/* Registers from ACP_SHA block */
#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index e132223b4c66..e1842f037083 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -62,12 +62,26 @@ int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
+ unsigned int count = ACP_HW_SEM_RETRY_COUNT;
+
+ while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) {
+ /* Wait until acquired HW Semaphore Lock or timeout*/
+ count--;
+ if (!count) {
+ dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
+ return -EINVAL;
+ }
+ }
acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
acp_ipc_host_msg_set(sdev);
/* Trigger host to dsp interrupt for the msg */
acpbus_trigger_host_to_dsp_swintr(adata);
+
+ /* Unlock or Release HW Semaphore */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0);
+
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
@@ -170,14 +184,6 @@ 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_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
-{
- /* TODO: Implement stream hw params to validate stream offset */
- return 0;
-}
-EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON);
-
int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return ACP_SCRATCH_MEMORY_ADDRESS;
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
index 2dc15ae38155..7ca51e0f3b1b 100644
--- a/sound/soc/sof/amd/acp-loader.c
+++ b/sound/soc/sof/amd/acp-loader.c
@@ -127,6 +127,12 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev
return;
}
+ /* Group Enable */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
+ ACP_SRAM_PTE_OFFSET | BIT(31));
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
+ PAGE_SIZE_4K_ENABLE);
+
for (page_idx = 0; page_idx < num_pages; page_idx++) {
low = lower_32_bits(addr);
high = upper_32_bits(addr);
@@ -136,6 +142,9 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev
offset += 8;
addr += PAGE_SIZE;
}
+
+ /* Flush ATU Cache after PTE Update */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
}
/* pre fw run operations */
diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c
index 5b23830cb1f3..0ba8ae46bd76 100644
--- a/sound/soc/sof/amd/acp-pcm.c
+++ b/sound/soc/sof/amd/acp-pcm.c
@@ -17,15 +17,17 @@
#include "acp-dsp-offset.h"
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params)
+ struct snd_pcm_hw_params *params,
+ struct snd_sof_platform_stream_params *platform_params)
{
- struct acp_dsp_stream *stream = substream->runtime->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct acp_dsp_stream *stream = runtime->private_data;
unsigned int buf_offset, index;
u32 size;
int ret;
- size = ipc_params->buffer.size;
- stream->num_pages = ipc_params->buffer.pages;
+ size = runtime->dma_bytes;
+ stream->num_pages = PFN_UP(runtime->dma_bytes);
stream->dmab = substream->runtime->dma_buffer_p;
ret = acp_dsp_stream_config(sdev, stream);
@@ -34,8 +36,9 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr
return ret;
}
- ipc_params->buffer.phy_addr = stream->reg_offset;
- ipc_params->stream_tag = stream->stream_tag;
+ platform_params->use_phy_address = true;
+ platform_params->phy_addr = stream->reg_offset;
+ platform_params->stream_tag = stream->stream_tag;
/* write buffer size of stream in scratch memory */
diff --git a/sound/soc/sof/amd/acp-stream.c b/sound/soc/sof/amd/acp-stream.c
index f2837bfbdb20..b3ca4a90dbf8 100644
--- a/sound/soc/sof/amd/acp-stream.c
+++ b/sound/soc/sof/amd/acp-stream.c
@@ -115,6 +115,9 @@ int acp_dsp_stream_config(struct snd_sof_dev *sdev, struct acp_dsp_stream *strea
offset += 8;
}
+ /* Flush ATU Cache after PTE Update */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
+
return 0;
}
diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c
index fa4da8947186..903b6cc3dda3 100644
--- a/sound/soc/sof/amd/acp-trace.c
+++ b/sound/soc/sof/amd/acp-trace.c
@@ -34,51 +34,31 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON);
-static int acp_sof_trace_prepare(struct snd_sof_dev *sdev,
- struct sof_ipc_dma_trace_params_ext *params)
+int acp_sof_trace_init(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct acp_dsp_stream *stream;
struct acp_dev_data *adata;
int ret;
adata = sdev->pdata->hw_pdata;
- stream = adata->dtrace_stream;
+ stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM);
+ if (!stream)
+ return -ENODEV;
+
stream->dmab = &sdev->dmatb;
stream->num_pages = NUM_PAGES;
ret = acp_dsp_stream_config(sdev, stream);
if (ret < 0) {
- dev_err(sdev->dev, "Failed to configure trace stream\n");
+ acp_dsp_stream_put(sdev, stream);
return ret;
}
- params->buffer.phy_addr = stream->reg_offset;
- params->stream_tag = stream->stream_tag;
-
- return 0;
-}
-
-int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
-{
- struct sof_ipc_dma_trace_params_ext *params;
- struct acp_dsp_stream *stream;
- struct acp_dev_data *adata;
- int ret;
-
- adata = sdev->pdata->hw_pdata;
- stream = acp_dsp_stream_get(sdev, ACP_LOGGER_STREAM);
- if (!stream)
- return -ENODEV;
-
adata->dtrace_stream = stream;
- params = container_of(stream_tag, struct sof_ipc_dma_trace_params_ext, stream_tag);
- ret = acp_sof_trace_prepare(sdev, params);
- if (ret < 0) {
- acp_dsp_stream_put(sdev, stream);
- return ret;
- }
+ dtrace_params->stream_tag = stream->stream_tag;
+ dtrace_params->buffer.phy_addr = stream->reg_offset;
- *stream_tag = stream->stream_tag;
return 0;
}
EXPORT_SYMBOL_NS(acp_sof_trace_init, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index fe9b7dc5bc86..71d71c152342 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -36,19 +36,6 @@ static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data)
return 0;
}
-static void configure_acp_groupregisters(struct acp_dev_data *adata)
-{
- struct snd_sof_dev *sdev = adata->dev;
-
- /* Group Enable */
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_BASE_ADDR_GRP_1,
- ACP_SRAM_PTE_OFFSET | BIT(31));
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1,
- PAGE_SIZE_4K_ENABLE);
-
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACPAXI2AXI_ATU_CTRL, ACP_ATU_CACHE_INVALID);
-}
-
static void init_dma_descriptor(struct acp_dev_data *adata)
{
struct snd_sof_dev *sdev = adata->dev;
@@ -264,7 +251,6 @@ static int acp_memory_init(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_CNTL,
ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
- configure_acp_groupregisters(adata);
init_dma_descriptor(adata);
return 0;
@@ -273,7 +259,7 @@ static int acp_memory_init(struct snd_sof_dev *sdev)
static irqreturn_t acp_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
- unsigned int val;
+ unsigned int val, count = ACP_HW_SEM_RETRY_COUNT;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_EXTERNAL_INTR_STAT);
if (val & ACP_SHA_STAT) {
@@ -284,9 +270,22 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT);
if (val & ACP_DSP_TO_HOST_IRQ) {
+ while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) {
+ /* Wait until acquired HW Semaphore lock or timeout */
+ count--;
+ if (!count) {
+ dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
+ return IRQ_NONE;
+ }
+ }
+
sof_ops(sdev)->irq_thread(irq, sdev);
val |= ACP_DSP_TO_HOST_IRQ;
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP_SW_INTR_STAT, val);
+
+ /* Unlock or Release HW Semaphore */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0);
+
return IRQ_HANDLED;
}
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index a2f8e4219066..35e46fe6676a 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -17,6 +17,7 @@
#define ACP_DSP_BAR 0
+#define ACP_HW_SEM_RETRY_COUNT 10000
#define ACP_REG_POLL_INTERVAL 500
#define ACP_REG_POLL_TIMEOUT_US 2000
#define ACP_DMA_COMPLETE_TIMEOUT_US 5000
@@ -185,8 +186,6 @@ int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg);
int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev);
int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
-int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply);
void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes);
@@ -202,7 +201,8 @@ int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stre
int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream);
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params);
+ struct snd_pcm_hw_params *params,
+ struct snd_sof_platform_stream_params *platform_params);
extern const struct snd_sof_dsp_ops sof_renoir_ops;
@@ -210,7 +210,8 @@ extern const struct snd_sof_dsp_ops sof_renoir_ops;
int snd_amd_acp_find_config(struct pci_dev *pci);
/* Trace */
-int acp_sof_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
+int acp_sof_trace_init(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params);
int acp_sof_trace_release(struct snd_sof_dev *sdev);
struct sof_amd_acp_desc {
diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c
index c3ecb9e9d5ba..409fd57448b8 100644
--- a/sound/soc/sof/amd/renoir.c
+++ b/sound/soc/sof/amd/renoir.c
@@ -150,7 +150,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = {
/*IPC */
.send_msg = acp_sof_ipc_send_msg,
.ipc_msg_data = acp_sof_ipc_msg_data,
- .ipc_pcm_params = acp_sof_ipc_pcm_params,
.get_mailbox_offset = acp_sof_ipc_get_mailbox_offset,
.irq_thread = acp_sof_ipc_irq_thread,
.fw_ready = sof_fw_ready,
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 01ca85f0b87f..a8e908e50101 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -9,6 +9,23 @@
#include <sound/compress_driver.h>
#include "sof-audio.h"
#include "sof-priv.h"
+#include "sof-utils.h"
+
+static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp,
+ u64 host_pos, u64 buffer_size)
+{
+ u64 prev_pos;
+ unsigned int copied;
+
+ div64_u64_rem(tstamp->copied_total, buffer_size, &prev_pos);
+
+ if (host_pos < prev_pos)
+ copied = (buffer_size - prev_pos) + host_pos;
+ else
+ copied = host_pos - prev_pos;
+
+ tstamp->copied_total += copied;
+}
static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
{
@@ -29,14 +46,18 @@ void snd_sof_compr_init_elapsed_work(struct work_struct *work)
*/
void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
{
- struct snd_soc_component *component;
struct snd_soc_pcm_runtime *rtd;
+ struct snd_compr_runtime *crtd;
+ struct snd_soc_component *component;
+ struct snd_compr_tstamp *tstamp;
struct snd_sof_pcm *spcm;
if (!cstream)
return;
rtd = cstream->private_data;
+ crtd = cstream->runtime;
+ tstamp = crtd->private_data;
component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -46,6 +67,257 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
return;
}
+ sof_set_transferred_bytes(tstamp, spcm->stream[cstream->direction].posn.host_posn,
+ crtd->buffer_size);
+
/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);
}
+
+static int create_page_table(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ unsigned char *dma_area, size_t size)
+{
+ struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ int dir = cstream->direction;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ return snd_sof_create_page_table(component->dev, dmab,
+ spcm->stream[dir].page_table.area, size);
+}
+
+static int sof_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_compr_runtime *crtd = cstream->runtime;
+ struct snd_compr_tstamp *tstamp;
+ struct snd_sof_pcm *spcm;
+ int dir;
+
+ tstamp = kzalloc(sizeof(*tstamp), GFP_KERNEL);
+ if (!tstamp)
+ return -ENOMEM;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm) {
+ kfree(tstamp);
+ return -EINVAL;
+ }
+
+ dir = cstream->direction;
+
+ if (spcm->stream[dir].cstream) {
+ kfree(tstamp);
+ return -EBUSY;
+ }
+
+ spcm->stream[dir].cstream = cstream;
+ spcm->stream[dir].posn.host_posn = 0;
+ spcm->stream[dir].posn.dai_posn = 0;
+ spcm->prepared[dir] = false;
+
+ crtd->private_data = tstamp;
+
+ return 0;
+}
+
+static int sof_compr_free(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_compr_tstamp *tstamp = cstream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct sof_ipc_stream stream;
+ struct sof_ipc_reply reply;
+ struct snd_sof_pcm *spcm;
+ int ret = 0;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ stream.hdr.size = sizeof(stream);
+ stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
+ stream.comp_id = spcm->stream[cstream->direction].comp_id;
+
+ if (spcm->prepared[cstream->direction]) {
+ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
+ &stream, sizeof(stream),
+ &reply, sizeof(reply));
+ if (!ret)
+ spcm->prepared[cstream->direction] = false;
+ }
+
+ cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work);
+ spcm->stream[cstream->direction].cstream = NULL;
+ kfree(tstamp);
+
+ return ret;
+}
+
+static int sof_compr_set_params(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream, struct snd_compr_params *params)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_compr_runtime *crtd = cstream->runtime;
+ struct sof_ipc_pcm_params_reply ipc_params_reply;
+ struct snd_compr_tstamp *tstamp;
+ struct sof_ipc_pcm_params pcm;
+ struct snd_sof_pcm *spcm;
+ int ret;
+
+ tstamp = crtd->private_data;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+
+ if (!spcm)
+ return -EINVAL;
+
+ cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+ cstream->dma_buffer.dev.dev = sdev->dev;
+ ret = snd_compr_malloc_pages(cstream, crtd->buffer_size);
+ if (ret < 0)
+ return ret;
+
+ ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes);
+ if (ret < 0)
+ return ret;
+
+ memset(&pcm, 0, sizeof(pcm));
+
+ pcm.params.buffer.pages = PFN_UP(crtd->dma_bytes);
+ pcm.hdr.size = sizeof(pcm);
+ pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
+
+ pcm.comp_id = spcm->stream[cstream->direction].comp_id;
+ pcm.params.hdr.size = sizeof(pcm.params);
+ pcm.params.buffer.phy_addr = spcm->stream[cstream->direction].page_table.addr;
+ pcm.params.buffer.size = crtd->dma_bytes;
+ pcm.params.direction = cstream->direction;
+ pcm.params.channels = params->codec.ch_out;
+ pcm.params.rate = params->codec.sample_rate;
+ pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
+ pcm.params.sample_container_bytes =
+ snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3;
+ pcm.params.host_period_bytes = params->buffer.fragment_size;
+
+ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ &ipc_params_reply, sizeof(ipc_params_reply));
+ if (ret < 0) {
+ dev_err(component->dev, "error ipc failed\n");
+ return ret;
+ }
+
+ tstamp->byte_offset = sdev->stream_box.offset + ipc_params_reply.posn_offset;
+ tstamp->sampling_rate = params->codec.sample_rate;
+
+ spcm->prepared[cstream->direction] = true;
+
+ return 0;
+}
+
+static int sof_compr_get_params(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream, struct snd_codec *params)
+{
+ /* TODO: we don't query the supported codecs for now, if the
+ * application asks for an unsupported codec the set_params() will fail.
+ */
+ return 0;
+}
+
+static int sof_compr_trigger(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream, int cmd)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct sof_ipc_stream stream;
+ struct sof_ipc_reply reply;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ stream.hdr.size = sizeof(stream);
+ stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
+ stream.comp_id = spcm->stream[cstream->direction].comp_id;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
+ break;
+ default:
+ dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd);
+ break;
+ }
+
+ return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd,
+ &stream, sizeof(stream),
+ &reply, sizeof(reply));
+}
+
+static int sof_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ unsigned int offset, n;
+ void *ptr;
+ int ret;
+
+ if (count > rtd->buffer_size)
+ count = rtd->buffer_size;
+
+ div_u64_rem(rtd->total_bytes_available, rtd->buffer_size, &offset);
+ ptr = rtd->dma_area + offset;
+ n = rtd->buffer_size - offset;
+
+ if (count < n) {
+ ret = copy_from_user(ptr, buf, count);
+ } else {
+ ret = copy_from_user(ptr, buf, n);
+ ret += copy_from_user(rtd->dma_area, buf + n, count - n);
+ }
+
+ return count - ret;
+}
+
+static int sof_compr_pointer(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_compr_tstamp *pstamp = cstream->runtime->private_data;
+
+ tstamp->sampling_rate = pstamp->sampling_rate;
+ tstamp->copied_total = pstamp->copied_total;
+
+ return 0;
+}
+
+struct snd_compress_ops sof_compressed_ops = {
+ .open = sof_compr_open,
+ .free = sof_compr_free,
+ .set_params = sof_compr_set_params,
+ .get_params = sof_compr_get_params,
+ .trigger = sof_compr_trigger,
+ .pointer = sof_compr_pointer,
+ .copy = sof_compr_copy,
+};
+EXPORT_SYMBOL(sof_compressed_ops);
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index ef61936dad59..de1778c4002b 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -45,68 +45,17 @@ static void update_mute_led(struct snd_sof_control *scontrol,
#endif
}
-static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
-{
- if (value >= size)
- return volume_map[size - 1];
-
- return volume_map[value];
-}
-
-static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- if (volume_map[i] >= value)
- return i;
- }
-
- return i - 1;
-}
-
-static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
-{
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct snd_soc_component *scomp = scontrol->scomp;
- int ret;
-
- if (!scontrol->comp_data_dirty)
- return;
-
- if (!pm_runtime_active(scomp->dev))
- return;
-
- /* set the ABI header values */
- cdata->data->magic = SOF_ABI_MAGIC;
- cdata->data->abi = SOF_ABI_VERSION;
-
- /* refresh the component data from DSP */
- scontrol->comp_data_dirty = false;
- ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to get control data: %d\n", ret);
- /* Set the flag to re-try next time to get the data */
- scontrol->comp_data_dirty = true;
- }
-}
-
int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_mixer_control *sm =
- (struct soc_mixer_control *)kcontrol->private_value;
+ struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
-
- snd_sof_refresh_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;
- /* read back each channel */
- for (i = 0; i < channels; i++)
- ucontrol->value.integer.value[i] =
- ipc_to_mixer(cdata->chanv[i].value,
- scontrol->volume_table, sm->max + 1);
+ if (tplg_ops->control->volume_get)
+ return tplg_ops->control->volume_get(scontrol, ucontrol);
return 0;
}
@@ -114,28 +63,16 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_mixer_control *sm =
- (struct soc_mixer_control *)kcontrol->private_value;
+ struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
- bool change = false;
- u32 value;
-
- /* update each channel */
- for (i = 0; i < channels; i++) {
- value = mixer_to_ipc(ucontrol->value.integer.value[i],
- scontrol->volume_table, sm->max + 1);
- change = change || (value != cdata->chanv[i].value);
- cdata->chanv[i].channel = i;
- cdata->chanv[i].value = value;
- }
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* notify DSP of mixer updates */
- if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol, true);
- return change;
+ if (tplg_ops->control->volume_put)
+ return tplg_ops->control->volume_put(scontrol, ucontrol);
+
+ return false;
}
int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
@@ -163,17 +100,14 @@ int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info
int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_mixer_control *sm =
- (struct soc_mixer_control *)kcontrol->private_value;
+ struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
-
- snd_sof_refresh_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;
- /* read back each channel */
- for (i = 0; i < channels; i++)
- ucontrol->value.integer.value[i] = cdata->chanv[i].value;
+ if (tplg_ops->control->switch_get)
+ return tplg_ops->control->switch_get(scontrol, ucontrol);
return 0;
}
@@ -181,47 +115,32 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_mixer_control *sm =
- (struct soc_mixer_control *)kcontrol->private_value;
+ struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value;
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
- bool change = false;
- u32 value;
-
- /* update each channel */
- for (i = 0; i < channels; i++) {
- value = ucontrol->value.integer.value[i];
- change = change || (value != cdata->chanv[i].value);
- cdata->chanv[i].channel = i;
- cdata->chanv[i].value = value;
- }
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
if (scontrol->led_ctl.use_led)
update_mute_led(scontrol, kcontrol, ucontrol);
- /* notify DSP of mixer updates */
- if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol, true);
+ if (tplg_ops->control->switch_put)
+ return tplg_ops->control->switch_put(scontrol, ucontrol);
- return change;
+ return false;
}
int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_enum *se =
- (struct soc_enum *)kcontrol->private_value;
+ struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
struct snd_sof_control *scontrol = se->dobj.private;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
-
- snd_sof_refresh_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;
- /* read back each channel */
- for (i = 0; i < channels; i++)
- ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
+ if (tplg_ops->control->enum_get)
+ return tplg_ops->control->enum_get(scontrol, ucontrol);
return 0;
}
@@ -229,62 +148,29 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_enum *se =
- (struct soc_enum *)kcontrol->private_value;
+ struct soc_enum *se = (struct soc_enum *)kcontrol->private_value;
struct snd_sof_control *scontrol = se->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- unsigned int i, channels = scontrol->num_channels;
- bool change = false;
- u32 value;
-
- /* update each channel */
- for (i = 0; i < channels; i++) {
- value = ucontrol->value.enumerated.item[i];
- change = change || (value != cdata->chanv[i].value);
- cdata->chanv[i].channel = i;
- cdata->chanv[i].value = value;
- }
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* notify DSP of enum updates */
- if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol, true);
+ if (tplg_ops->control->enum_put)
+ return tplg_ops->control->enum_put(scontrol, ucontrol);
- return change;
+ return false;
}
int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_bytes_ext *be =
- (struct soc_bytes_ext *)kcontrol->private_value;
+ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct sof_abi_hdr *data = cdata->data;
- size_t size;
-
- snd_sof_refresh_control(scontrol);
-
- if (be->max > sizeof(ucontrol->value.bytes.data)) {
- dev_err_ratelimited(scomp->dev,
- "error: data max %d exceeds ucontrol data array size\n",
- be->max);
- return -EINVAL;
- }
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > be->max - sizeof(*data)) {
- dev_err_ratelimited(scomp->dev,
- "error: %u bytes of control data is invalid, max is %zu\n",
- data->size, be->max - sizeof(*data));
- return -EINVAL;
- }
-
- size = data->size + sizeof(*data);
-
- /* copy back to kcontrol */
- memcpy(ucontrol->value.bytes.data, data, size);
+ if (tplg_ops->control->bytes_get)
+ return tplg_ops->control->bytes_get(scontrol, ucontrol);
return 0;
}
@@ -292,37 +178,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct soc_bytes_ext *be =
- (struct soc_bytes_ext *)kcontrol->private_value;
+ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct sof_abi_hdr *data = cdata->data;
- size_t size;
-
- if (be->max > sizeof(ucontrol->value.bytes.data)) {
- dev_err_ratelimited(scomp->dev,
- "error: data max %d exceeds ucontrol data array size\n",
- be->max);
- return -EINVAL;
- }
-
- /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > be->max - sizeof(*data)) {
- dev_err_ratelimited(scomp->dev,
- "error: data size too big %u bytes max is %zu\n",
- data->size, be->max - sizeof(*data));
- return -EINVAL;
- }
-
- size = data->size + sizeof(*data);
-
- /* copy from kcontrol */
- memcpy(data, ucontrol->value.bytes.data, size);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* notify DSP of byte control updates */
- if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol, true);
+ if (tplg_ops->control->bytes_put)
+ return tplg_ops->control->bytes_put(scontrol, ucontrol);
return 0;
}
@@ -331,74 +194,18 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
const unsigned int __user *binary_data,
unsigned int size)
{
- struct soc_bytes_ext *be =
- (struct soc_bytes_ext *)kcontrol->private_value;
+ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct snd_ctl_tlv header;
- const struct snd_ctl_tlv __user *tlvd =
- (const struct snd_ctl_tlv __user *)binary_data;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
/* make sure we have at least a header */
if (size < sizeof(struct snd_ctl_tlv))
return -EINVAL;
- /*
- * The beginning of bytes data contains a header from where
- * the length (as bytes) is needed to know the correct copy
- * length of data from tlvd->tlv.
- */
- if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))
- return -EFAULT;
-
- /* make sure TLV info is consistent */
- if (header.length + sizeof(struct snd_ctl_tlv) > size) {
- dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n",
- header.length, sizeof(struct snd_ctl_tlv), size);
- return -EINVAL;
- }
-
- /* be->max is coming from topology */
- if (header.length > be->max) {
- dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n",
- header.length, be->max);
- return -EINVAL;
- }
-
- /* Check that header id matches the command */
- if (header.numid != cdata->cmd) {
- dev_err_ratelimited(scomp->dev,
- "error: incorrect numid %d\n",
- header.numid);
- return -EINVAL;
- }
-
- if (copy_from_user(cdata->data, tlvd->tlv, header.length))
- return -EFAULT;
-
- if (cdata->data->magic != SOF_ABI_MAGIC) {
- dev_err_ratelimited(scomp->dev,
- "error: Wrong ABI magic 0x%08x.\n",
- cdata->data->magic);
- return -EINVAL;
- }
-
- if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
- dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n",
- cdata->data->abi);
- return -EINVAL;
- }
-
- /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) {
- dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n");
- return -EINVAL;
- }
-
- /* notify DSP of byte control updates */
- if (pm_runtime_active(scomp->dev))
- snd_sof_ipc_set_get_comp_data(scontrol, true);
+ if (tplg_ops->control->bytes_ext_put)
+ return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size);
return 0;
}
@@ -409,67 +216,24 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct snd_ctl_tlv header;
- struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
- size_t data_size;
- int ret;
- int err;
-
- /*
- * Decrement the limit by ext bytes header size to
- * ensure the user space buffer is not exceeded.
- */
- if (size < sizeof(struct snd_ctl_tlv))
- return -ENOSPC;
- size -= sizeof(struct snd_ctl_tlv);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ int ret, err;
ret = pm_runtime_get_sync(scomp->dev);
if (ret < 0 && ret != -EACCES) {
- dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret);
+ dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret);
pm_runtime_put_noidle(scomp->dev);
return ret;
}
- /* set the ABI header values */
- cdata->data->magic = SOF_ABI_MAGIC;
- cdata->data->abi = SOF_ABI_VERSION;
- /* get all the component data from DSP */
- ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
- if (ret < 0)
- goto out;
-
- /* check data size doesn't exceed max coming from topology */
- if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) {
- dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n",
- cdata->data->size,
- be->max - sizeof(struct sof_abi_hdr));
- ret = -EINVAL;
- goto out;
- }
-
- data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
-
- /* make sure we don't exceed size provided by user space for data */
- if (data_size > size) {
- ret = -ENOSPC;
- goto out;
- }
-
- header.numid = cdata->cmd;
- header.length = data_size;
- if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) {
- ret = -EFAULT;
- goto out;
- }
+ if (tplg_ops->control->bytes_ext_volatile_get)
+ ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
- if (copy_to_user(tlvd->tlv, cdata->data, data_size))
- ret = -EFAULT;
-out:
pm_runtime_mark_last_busy(scomp->dev);
err = pm_runtime_put_autosuspend(scomp->dev);
if (err < 0)
- dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err);
+ dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err);
return ret;
}
@@ -478,195 +242,14 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
unsigned int __user *binary_data,
unsigned int size)
{
- struct soc_bytes_ext *be =
- (struct soc_bytes_ext *)kcontrol->private_value;
+ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value;
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
- struct snd_ctl_tlv header;
- struct snd_ctl_tlv __user *tlvd =
- (struct snd_ctl_tlv __user *)binary_data;
- size_t data_size;
-
- snd_sof_refresh_control(scontrol);
-
- /*
- * Decrement the limit by ext bytes header size to
- * ensure the user space buffer is not exceeded.
- */
- if (size < sizeof(struct snd_ctl_tlv))
- return -ENOSPC;
- size -= sizeof(struct snd_ctl_tlv);
-
- /* set the ABI header values */
- cdata->data->magic = SOF_ABI_MAGIC;
- cdata->data->abi = SOF_ABI_VERSION;
-
- /* check data size doesn't exceed max coming from topology */
- if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) {
- dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n",
- cdata->data->size,
- be->max - sizeof(struct sof_abi_hdr));
- return -EINVAL;
- }
-
- data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* make sure we don't exceed size provided by user space for data */
- if (data_size > size)
- return -ENOSPC;
-
- header.numid = cdata->cmd;
- header.length = data_size;
- if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
- return -EFAULT;
-
- if (copy_to_user(tlvd->tlv, cdata->data, data_size))
- return -EFAULT;
+ if (tplg_ops->control->bytes_ext_get)
+ return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size);
return 0;
}
-
-static void snd_sof_update_control(struct snd_sof_control *scontrol,
- struct sof_ipc_ctrl_data *cdata)
-{
- struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *local_cdata;
- int i;
-
- local_cdata = scontrol->control_data;
-
- if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
- if (cdata->num_elems != local_cdata->data->size) {
- dev_err(scomp->dev,
- "error: cdata binary size mismatch %u - %u\n",
- cdata->num_elems, local_cdata->data->size);
- return;
- }
-
- /* copy the new binary data */
- memcpy(local_cdata->data, cdata->data, cdata->num_elems);
- } else if (cdata->num_elems != scontrol->num_channels) {
- dev_err(scomp->dev,
- "error: cdata channel count mismatch %u - %d\n",
- cdata->num_elems, scontrol->num_channels);
- } else {
- /* copy the new values */
- for (i = 0; i < cdata->num_elems; i++)
- local_cdata->chanv[i].value = cdata->chanv[i].value;
- }
-}
-
-void snd_sof_control_notify(struct snd_sof_dev *sdev,
- struct sof_ipc_ctrl_data *cdata)
-{
- struct snd_soc_dapm_widget *widget;
- struct snd_sof_control *scontrol;
- struct snd_sof_widget *swidget;
- struct snd_kcontrol *kc = NULL;
- struct soc_mixer_control *sm;
- struct soc_bytes_ext *be;
- size_t expected_size;
- struct soc_enum *se;
- bool found = false;
- int i, type;
-
- if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET ||
- cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) {
- dev_err(sdev->dev,
- "Component data is not supported in control notification\n");
- return;
- }
-
- /* Find the swidget first */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (swidget->comp_id == cdata->comp_id) {
- found = true;
- break;
- }
- }
-
- if (!found)
- return;
-
- /* Translate SOF cmd to TPLG type */
- switch (cdata->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_SWITCH:
- type = SND_SOC_TPLG_TYPE_MIXER;
- break;
- case SOF_CTRL_CMD_BINARY:
- type = SND_SOC_TPLG_TYPE_BYTES;
- break;
- case SOF_CTRL_CMD_ENUM:
- type = SND_SOC_TPLG_TYPE_ENUM;
- break;
- default:
- dev_err(sdev->dev, "error: unknown cmd %u\n", cdata->cmd);
- return;
- }
-
- widget = swidget->widget;
- for (i = 0; i < widget->num_kcontrols; i++) {
- /* skip non matching types or non matching indexes within type */
- if (widget->dobj.widget.kcontrol_type[i] == type &&
- widget->kcontrol_news[i].index == cdata->index) {
- kc = widget->kcontrols[i];
- break;
- }
- }
-
- if (!kc)
- return;
-
- switch (cdata->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_SWITCH:
- sm = (struct soc_mixer_control *)kc->private_value;
- scontrol = sm->dobj.private;
- break;
- case SOF_CTRL_CMD_BINARY:
- be = (struct soc_bytes_ext *)kc->private_value;
- scontrol = be->dobj.private;
- break;
- case SOF_CTRL_CMD_ENUM:
- se = (struct soc_enum *)kc->private_value;
- scontrol = se->dobj.private;
- break;
- default:
- return;
- }
-
- expected_size = sizeof(struct sof_ipc_ctrl_data);
- switch (cdata->type) {
- case SOF_CTRL_TYPE_VALUE_CHAN_GET:
- case SOF_CTRL_TYPE_VALUE_CHAN_SET:
- expected_size += cdata->num_elems *
- sizeof(struct sof_ipc_ctrl_value_chan);
- break;
- case SOF_CTRL_TYPE_DATA_GET:
- case SOF_CTRL_TYPE_DATA_SET:
- expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
- break;
- default:
- return;
- }
-
- if (cdata->rhdr.hdr.size != expected_size) {
- dev_err(sdev->dev, "error: component notification size mismatch\n");
- return;
- }
-
- if (cdata->num_elems)
- /*
- * The message includes the updated value/data, update the
- * control's local cache using the received notification
- */
- snd_sof_update_control(scontrol, cdata);
- else
- /* Mark the scontrol that the value/data is changed in SOF */
- scontrol->comp_data_dirty = true;
-
- snd_ctl_notify_one(swidget->scomp->card->snd_card,
- SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
-}
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 8f32b5b12b3e..e91631618bff 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -14,9 +14,6 @@
#include <sound/sof.h>
#include "sof-priv.h"
#include "ops.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "sof-probes.h"
-#endif
/* see SOF_DBG_ flags */
static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
@@ -122,6 +119,27 @@ out:
}
EXPORT_SYMBOL(sof_print_oops_and_stack);
+/* Helper to manage DSP state */
+void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
+{
+ if (sdev->fw_state == new_state)
+ return;
+
+ dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
+ sdev->fw_state = new_state;
+
+ switch (new_state) {
+ case SOF_FW_BOOT_NOT_STARTED:
+ case SOF_FW_BOOT_COMPLETE:
+ case SOF_FW_CRASHED:
+ sof_client_fw_state_dispatcher(sdev);
+ fallthrough;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(sof_set_fw_state);
+
/*
* FW Boot State Transition Diagram
*
@@ -266,6 +284,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_trace_err;
}
+ ret = sof_register_clients(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to register clients %d\n", ret);
+ goto sof_machine_err;
+ }
+
/*
* Some platforms in SOF, ex: BYT, may not have their platform PM
* callbacks set. Increment the usage count so as to
@@ -281,6 +305,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;
+sof_machine_err:
+ snd_sof_machine_unregister(sdev, plat_data);
fw_trace_err:
snd_sof_free_trace(sdev);
fw_run_err:
@@ -329,18 +355,13 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->pdata = plat_data;
sdev->first_boot = true;
- sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
-#endif
dev_set_drvdata(dev, sdev);
/* check all mandatory ops */
if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
!sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
!sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
- !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->ipc_pcm_params ||
- !sof_ops(sdev)->fw_ready) {
+ !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->fw_ready) {
dev_err(dev, "error: missing mandatory ops\n");
return -EINVAL;
}
@@ -349,10 +370,16 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list);
INIT_LIST_HEAD(&sdev->dai_list);
+ INIT_LIST_HEAD(&sdev->dai_link_list);
INIT_LIST_HEAD(&sdev->route_list);
+ INIT_LIST_HEAD(&sdev->ipc_client_list);
+ INIT_LIST_HEAD(&sdev->ipc_rx_handler_list);
+ INIT_LIST_HEAD(&sdev->fw_state_handler_list);
spin_lock_init(&sdev->ipc_lock);
spin_lock_init(&sdev->hw_lock);
mutex_init(&sdev->power_state_access);
+ mutex_init(&sdev->ipc_client_mutex);
+ mutex_init(&sdev->client_event_handler_mutex);
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
@@ -364,6 +391,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
else
sdev->boot_timeout = plat_data->desc->boot_timeout;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
+
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
INIT_WORK(&sdev->probe_work, sof_probe_work);
schedule_work(&sdev->probe_work);
@@ -392,6 +421,12 @@ int snd_sof_device_remove(struct device *dev)
cancel_work_sync(&sdev->probe_work);
/*
+ * Unregister any registered client device first before IPC and debugfs
+ * to allow client drivers to be removed cleanly
+ */
+ sof_unregister_clients(sdev);
+
+ /*
* Unregister machine driver. This will unbind the snd_card which
* will remove the component driver and unload the topology
* before freeing the snd_card.
@@ -407,16 +442,8 @@ int snd_sof_device_remove(struct device *dev)
snd_sof_ipc_free(sdev);
snd_sof_free_debug(sdev);
- }
-
- /*
- * Unregistering the machine driver results in unloading the topology.
- * Some widgets, ex: scheduler, attempt to power down the core they are
- * scheduled on, when they are unloaded. Therefore, the DSP must be
- * removed only after the topology has been unloaded.
- */
- if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED)
snd_sof_remove(sdev);
+ }
/* release firmware */
snd_sof_fw_unload(sdev);
@@ -428,10 +455,19 @@ EXPORT_SYMBOL(snd_sof_device_remove);
int snd_sof_device_shutdown(struct device *dev)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
+ struct snd_sof_pdata *pdata = sdev->pdata;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
+ /*
+ * make sure clients and machine driver(s) are unregistered to force
+ * all userspace devices to be closed prior to the DSP shutdown sequence
+ */
+ sof_unregister_clients(sdev);
+
+ snd_sof_machine_unregister(sdev, pdata);
+
if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
return snd_sof_shutdown(sdev);
@@ -443,3 +479,4 @@ MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:sof-audio");
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index 6d6757075f7c..7b1139961a99 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -19,433 +19,9 @@
#include "sof-priv.h"
#include "ops.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "sof-probes.h"
-
-/**
- * strsplit_u32 - Split string into sequence of u32 tokens
- * @buf: String to split into tokens.
- * @delim: String containing delimiter characters.
- * @tkns: Returned u32 sequence pointer.
- * @num_tkns: Returned number of tokens obtained.
- */
-static int
-strsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns)
-{
- char *s;
- u32 *data, *tmp;
- size_t count = 0;
- size_t cap = 32;
- int ret = 0;
-
- *tkns = NULL;
- *num_tkns = 0;
- data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- while ((s = strsep(buf, delim)) != NULL) {
- ret = kstrtouint(s, 0, data + count);
- if (ret)
- goto exit;
- if (++count >= cap) {
- cap *= 2;
- tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
- if (!tmp) {
- ret = -ENOMEM;
- goto exit;
- }
- data = tmp;
- }
- }
-
- if (!count)
- goto exit;
- *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
- if (*tkns == NULL) {
- ret = -ENOMEM;
- goto exit;
- }
- *num_tkns = count;
-
-exit:
- kfree(data);
- return ret;
-}
-
-static int tokenize_input(const char __user *from, size_t count,
- loff_t *ppos, u32 **tkns, size_t *num_tkns)
-{
- char *buf;
- int ret;
-
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = simple_write_to_buffer(buf, count, ppos, from, count);
- if (ret != count) {
- ret = ret >= 0 ? -EIO : ret;
- goto exit;
- }
-
- buf[count] = '\0';
- ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns);
-exit:
- kfree(buf);
- return ret;
-}
-
-static ssize_t probe_points_read(struct file *file,
- char __user *to, size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- struct sof_probe_point_desc *desc;
- size_t num_desc, len = 0;
- char *buf;
- int i, ret;
-
- if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
- dev_warn(sdev->dev, "no extractor stream running\n");
- return -ENOENT;
- }
-
- buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc);
- if (ret < 0)
- goto exit;
-
- for (i = 0; i < num_desc; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "Id: %#010x Purpose: %d Node id: %#x\n",
- desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
- if (ret < 0)
- goto free_desc;
- len += ret;
- }
-
- ret = simple_read_from_buffer(to, count, ppos, buf, len);
-free_desc:
- kfree(desc);
-exit:
- kfree(buf);
- return ret;
-}
-
-static ssize_t probe_points_write(struct file *file,
- const char __user *from, size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- struct sof_probe_point_desc *desc;
- size_t num_tkns, bytes;
- u32 *tkns;
- int ret;
-
- if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
- dev_warn(sdev->dev, "no extractor stream running\n");
- return -ENOENT;
- }
-
- ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
- if (ret < 0)
- return ret;
- bytes = sizeof(*tkns) * num_tkns;
- if (!num_tkns || (bytes % sizeof(*desc))) {
- ret = -EINVAL;
- goto exit;
- }
-
- desc = (struct sof_probe_point_desc *)tkns;
- ret = sof_ipc_probe_points_add(sdev,
- desc, bytes / sizeof(*desc));
- if (!ret)
- ret = count;
-exit:
- kfree(tkns);
- return ret;
-}
-
-static const struct file_operations probe_points_fops = {
- .open = simple_open,
- .read = probe_points_read,
- .write = probe_points_write,
- .llseek = default_llseek,
-};
-
-static ssize_t probe_points_remove_write(struct file *file,
- const char __user *from, size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- size_t num_tkns;
- u32 *tkns;
- int ret;
-
- if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) {
- dev_warn(sdev->dev, "no extractor stream running\n");
- return -ENOENT;
- }
-
- ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
- if (ret < 0)
- return ret;
- if (!num_tkns) {
- ret = -EINVAL;
- goto exit;
- }
-
- ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns);
- if (!ret)
- ret = count;
-exit:
- kfree(tkns);
- return ret;
-}
-
-static const struct file_operations probe_points_remove_fops = {
- .open = simple_open,
- .write = probe_points_remove_write,
- .llseek = default_llseek,
-};
-
-static int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev,
- const char *name, mode_t mode,
- const struct file_operations *fops)
-{
- struct snd_sof_dfsentry *dfse;
-
- dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
- if (!dfse)
- return -ENOMEM;
-
- dfse->type = SOF_DFSENTRY_TYPE_BUF;
- dfse->sdev = sdev;
-
- debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops);
- /* add to dfsentry list */
- list_add(&dfse->list, &sdev->dfsentry_list);
-
- return 0;
-}
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
-#define MAX_IPC_FLOOD_DURATION_MS 1000
-#define MAX_IPC_FLOOD_COUNT 10000
-#define IPC_FLOOD_TEST_RESULT_LEN 512
-
-static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev,
- struct snd_sof_dfsentry *dfse,
- bool flood_duration_test,
- unsigned long ipc_duration_ms,
- unsigned long ipc_count)
-{
- struct sof_ipc_cmd_hdr hdr;
- struct sof_ipc_reply reply;
- u64 min_response_time = U64_MAX;
- ktime_t start, end, test_end;
- u64 avg_response_time = 0;
- u64 max_response_time = 0;
- u64 ipc_response_time;
- int i = 0;
- int ret;
-
- /* configure test IPC */
- hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD;
- hdr.size = sizeof(hdr);
-
- /* set test end time for duration flood test */
- if (flood_duration_test)
- test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC;
-
- /* send test IPC's */
- while (1) {
- start = ktime_get();
- ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size,
- &reply, sizeof(reply));
- end = ktime_get();
-
- if (ret < 0)
- break;
-
- /* compute min and max response times */
- ipc_response_time = ktime_to_ns(ktime_sub(end, start));
- min_response_time = min(min_response_time, ipc_response_time);
- max_response_time = max(max_response_time, ipc_response_time);
-
- /* sum up response times */
- avg_response_time += ipc_response_time;
- i++;
-
- /* test complete? */
- if (flood_duration_test) {
- if (ktime_to_ns(end) >= test_end)
- break;
- } else {
- if (i == ipc_count)
- break;
- }
- }
-
- if (ret < 0)
- dev_err(sdev->dev,
- "error: ipc flood test failed at %d iterations\n", i);
-
- /* return if the first IPC fails */
- if (!i)
- return ret;
-
- /* compute average response time */
- do_div(avg_response_time, i);
-
- /* clear previous test output */
- memset(dfse->cache_buf, 0, IPC_FLOOD_TEST_RESULT_LEN);
-
- if (flood_duration_test) {
- dev_dbg(sdev->dev, "IPC Flood test duration: %lums\n",
- ipc_duration_ms);
- snprintf(dfse->cache_buf, IPC_FLOOD_TEST_RESULT_LEN,
- "IPC Flood test duration: %lums\n", ipc_duration_ms);
- }
-
- dev_dbg(sdev->dev,
- "IPC Flood count: %d, Avg response time: %lluns\n",
- i, avg_response_time);
- dev_dbg(sdev->dev, "Max response time: %lluns\n",
- max_response_time);
- dev_dbg(sdev->dev, "Min response time: %lluns\n",
- min_response_time);
-
- /* format output string */
- snprintf(dfse->cache_buf + strlen(dfse->cache_buf),
- IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf),
- "IPC Flood count: %d\nAvg response time: %lluns\n",
- i, avg_response_time);
-
- snprintf(dfse->cache_buf + strlen(dfse->cache_buf),
- IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf),
- "Max response time: %lluns\nMin response time: %lluns\n",
- max_response_time, min_response_time);
-
- return ret;
-}
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
-static ssize_t msg_inject_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct sof_ipc_reply *rhdr = dfse->msg_inject_rx;
-
- if (!rhdr->hdr.size || !count || *ppos)
- return 0;
-
- if (count > rhdr->hdr.size)
- count = rhdr->hdr.size;
-
- if (copy_to_user(buffer, dfse->msg_inject_rx, count))
- return -EFAULT;
-
- *ppos += count;
- return count;
-}
-
-static ssize_t msg_inject_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx;
- size_t size;
- int ret, err;
-
- if (*ppos)
- return 0;
-
- size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE,
- ppos, buffer, count);
- if (size != count)
- return size > 0 ? -EFAULT : size;
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0 && ret != -EACCES) {
- dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n",
- __func__, ret);
- pm_runtime_put_noidle(sdev->dev);
- goto out;
- }
-
- /* send the message */
- memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE);
- ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count,
- dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n",
- __func__, err);
-
- /* return size if test is successful */
- if (ret >= 0)
- ret = size;
-
-out:
- return ret;
-}
-
-static const struct file_operations msg_inject_fops = {
- .open = simple_open,
- .read = msg_inject_read,
- .write = msg_inject_write,
- .llseek = default_llseek,
-};
-
-static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev,
- const char *name, mode_t mode,
- const struct file_operations *fops)
-{
- struct snd_sof_dfsentry *dfse;
-
- dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
- if (!dfse)
- return -ENOMEM;
-
- /* pre allocate the tx and rx buffers */
- dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- if (!dfse->msg_inject_tx || !dfse->msg_inject_rx)
- return -ENOMEM;
-
- dfse->type = SOF_DFSENTRY_TYPE_BUF;
- dfse->sdev = sdev;
-
- debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops);
- /* add to dfsentry list */
- list_add(&dfse->list, &sdev->dfsentry_list);
-
- return 0;
-}
-#endif
-
static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
- struct snd_sof_dfsentry *dfse = file->private_data;
- struct snd_sof_dev *sdev = dfse->sdev;
- unsigned long ipc_duration_ms = 0;
- bool flood_duration_test = false;
- unsigned long ipc_count = 0;
- struct dentry *dentry;
- int err;
-#endif
size_t size;
char *string;
int ret;
@@ -457,78 +33,6 @@ static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
size = simple_write_to_buffer(string, count, ppos, buffer, count);
ret = size;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
- /*
- * write op is only supported for ipc_flood_count or
- * ipc_flood_duration_ms debugfs entries atm.
- * ipc_flood_count floods the DSP with the number of IPC's specified.
- * ipc_duration_ms test floods the DSP for the time specified
- * in the debugfs entry.
- */
- dentry = file->f_path.dentry;
- if (strcmp(dentry->d_name.name, "ipc_flood_count") &&
- strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!strcmp(dentry->d_name.name, "ipc_flood_duration_ms"))
- flood_duration_test = true;
-
- /* test completion criterion */
- if (flood_duration_test)
- ret = kstrtoul(string, 0, &ipc_duration_ms);
- else
- ret = kstrtoul(string, 0, &ipc_count);
- if (ret < 0)
- goto out;
-
- /* limit max duration/ipc count for flood test */
- if (flood_duration_test) {
- if (!ipc_duration_ms) {
- ret = size;
- goto out;
- }
-
- /* find the minimum. min() is not used to avoid warnings */
- if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS)
- ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS;
- } else {
- if (!ipc_count) {
- ret = size;
- goto out;
- }
-
- /* find the minimum. min() is not used to avoid warnings */
- if (ipc_count > MAX_IPC_FLOOD_COUNT)
- ipc_count = MAX_IPC_FLOOD_COUNT;
- }
-
- ret = pm_runtime_get_sync(sdev->dev);
- if (ret < 0 && ret != -EACCES) {
- dev_err_ratelimited(sdev->dev,
- "error: debugfs write failed to resume %d\n",
- ret);
- pm_runtime_put_noidle(sdev->dev);
- goto out;
- }
-
- /* flood test */
- ret = sof_debug_ipc_flood_test(sdev, dfse, flood_duration_test,
- ipc_duration_ms, ipc_count);
-
- pm_runtime_mark_last_busy(sdev->dev);
- err = pm_runtime_put_autosuspend(sdev->dev);
- if (err < 0)
- dev_err_ratelimited(sdev->dev,
- "error: debugfs write failed to idle %d\n",
- err);
-
- /* return size if test is successful */
- if (ret >= 0)
- ret = size;
-out:
-#endif
kfree(string);
return ret;
}
@@ -544,24 +48,6 @@ static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer,
int size;
u8 *buf;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
- struct dentry *dentry;
-
- dentry = file->f_path.dentry;
- if ((!strcmp(dentry->d_name.name, "ipc_flood_count") ||
- !strcmp(dentry->d_name.name, "ipc_flood_duration_ms"))) {
- if (*ppos)
- return 0;
-
- count = strlen(dfse->cache_buf);
- size_ret = copy_to_user(buffer, dfse->cache_buf, count);
- if (size_ret)
- return -EFAULT;
-
- *ppos += count;
- return count;
- }
-#endif
size = dfse->size;
/* validate position & count */
@@ -719,19 +205,6 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
dfse->size = size;
dfse->sdev = sdev;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
- if (!strncmp(name, "ipc_flood", strlen("ipc_flood"))) {
- /*
- * cache_buf is unused for SOF_DFSENTRY_TYPE_BUF debugfs entries.
- * So, use it to save the results of the last IPC flood test.
- */
- dfse->cache_buf = devm_kzalloc(sdev->dev, IPC_FLOOD_TEST_RESULT_LEN,
- GFP_KERNEL);
- if (!dfse->cache_buf)
- return -ENOMEM;
- }
-#endif
-
debugfs_create_file(name, mode, sdev->debugfs_root, dfse,
&sof_dfs_fops);
/* add to dfsentry list */
@@ -881,44 +354,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
return err;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- err = snd_sof_debugfs_probe_item(sdev, "probe_points",
- 0644, &probe_points_fops);
- if (err < 0)
- return err;
- err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove",
- 0200, &probe_points_remove_fops);
- if (err < 0)
- return err;
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
- /* create read-write ipc_flood_count debugfs entry */
- err = snd_sof_debugfs_buf_item(sdev, NULL, 0,
- "ipc_flood_count", 0666);
-
- /* errors are only due to memory allocation, not debugfs */
- if (err < 0)
- return err;
-
- /* create read-write ipc_flood_duration_ms debugfs entry */
- err = snd_sof_debugfs_buf_item(sdev, NULL, 0,
- "ipc_flood_duration_ms", 0666);
-
- /* errors are only due to memory allocation, not debugfs */
- if (err < 0)
- return err;
-#endif
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
- err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644,
- &msg_inject_fops);
-
- /* errors are only due to memory allocation, not debugfs */
- if (err < 0)
- return err;
-#endif
-
return 0;
}
EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
@@ -960,7 +395,7 @@ static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *lev
void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags)
{
- char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR;
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS);
if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all)
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index f6baecbb57fb..825bd2b9b7a1 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -509,7 +509,7 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = {
.get_window_offset = imx8_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
@@ -572,7 +572,7 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = {
.get_window_offset = imx8_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index 788e77bcb603..803d6be6b4fb 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -224,6 +224,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
}
ret = of_address_to_resource(res_node, 0, &res);
+ of_node_put(res_node);
if (ret) {
dev_err(&pdev->dev, "failed to get reserved region address\n");
goto exit_pdev_unregister;
@@ -434,7 +435,7 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = {
.get_window_offset = imx8m_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* module loading */
.load_module = snd_sof_parse_module_memcpy,
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 88b6176af021..b53f216d4ecc 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -215,6 +215,7 @@ config SND_SOC_SOF_HDA_COMMON
select SND_SOC_SOF_PCI_DEV
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
+ select SND_SOC_SOF_HDA_PROBES
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
@@ -240,15 +241,6 @@ config SND_SOC_SOF_HDA_AUDIO_CODEC
Say Y if you want to enable HDAudio codecs with SOF.
If unsure select "N".
-config SND_SOC_SOF_HDA_PROBES
- bool "SOF enable probes over HDA"
- depends on SND_SOC_SOF_DEBUG_PROBES
- help
- This option enables the data probing for Intel(R)
- Skylake and newer platforms.
- Say Y if you want to enable probes.
- If unsure, select "N".
-
endif ## SND_SOC_SOF_HDA_COMMON
config SND_SOC_SOF_HDA_LINK_BASELINE
@@ -266,6 +258,15 @@ config SND_SOC_SOF_HDA
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
+config SND_SOC_SOF_HDA_PROBES
+ bool
+ select SND_SOC_SOF_DEBUG_PROBES
+ help
+ The option enables the data probing for Intel(R) Skylake and newer
+ (HDA) platforms.
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
tristate
select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 810b8b6748a0..6721c8f95161 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -56,7 +56,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.get_window_offset = hda_dsp_ipc_get_window_offset,
.ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
+ .set_stream_data_offset = hda_set_stream_data_offset,
/* machine driver */
.machine_select = hda_machine_select,
@@ -80,15 +80,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
@@ -110,6 +101,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index d627b7498d5e..fb9682b2fe32 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -596,7 +596,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = {
.get_window_offset = bdw_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
.machine_select = bdw_machine_select,
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index dcfeaedb8fd5..bb84a4aa587a 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -250,7 +250,7 @@ static const struct snd_sof_dsp_ops sof_byt_ops = {
.get_window_offset = atom_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
.machine_select = atom_machine_select,
@@ -332,7 +332,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = {
.get_window_offset = atom_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
.machine_select = atom_machine_select,
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index e615125d575e..6a96470b967f 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -161,11 +161,9 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev)
static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg,
u32 *dr, u32 *dd)
{
- struct sof_ipc_pm_gate *pm_gate;
-
- if (msg->header == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
- pm_gate = msg->msg_data;
+ struct sof_ipc_pm_gate *pm_gate = msg->msg_data;
+ if (pm_gate->hdr.cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
/* send the compact message via the primary register */
*dr = HDA_IPC_MSG_COMPACT | HDA_IPC_PM_GATE;
@@ -276,7 +274,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.get_window_offset = hda_dsp_ipc_get_window_offset,
.ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
+ .set_stream_data_offset = hda_set_stream_data_offset,
/* machine driver */
.machine_select = hda_machine_select,
@@ -300,15 +298,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
@@ -330,6 +319,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index cd12589355ef..f9cb9f1f0237 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -16,10 +16,6 @@
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "../sof-probes.h"
-#endif
-
struct hda_pipe_params {
u32 ch;
u32 s_freq;
@@ -59,8 +55,10 @@ static struct hdac_ext_stream *
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct sof_intel_hda_stream *hda_stream;
+ const struct sof_intel_dsp_desc *chip;
+ struct snd_sof_dev *sdev;
struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
+ struct hdac_stream *hstream = NULL;
int stream_dir = substream->stream;
@@ -70,28 +68,39 @@ static struct hdac_ext_stream *
}
spin_lock_irq(&bus->reg_lock);
- list_for_each_entry(stream, &bus->stream_list, list) {
- struct hdac_ext_stream *hstream =
- stream_to_hdac_ext_stream(stream);
- if (stream->direction != substream->stream)
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream =
+ stream_to_hdac_ext_stream(hstream);
+ if (hstream->direction != substream->stream)
continue;
- hda_stream = hstream_to_sof_hda_stream(hstream);
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ sdev = hda_stream->sdev;
+ chip = get_chip_info(sdev->pdata);
/* check if link is available */
- if (!hstream->link_locked) {
- if (stream->opened) {
+ if (!hext_stream->link_locked) {
+ /*
+ * choose the first available link for platforms that do not have the
+ * PROCEN_FMT_QUIRK set.
+ */
+ if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
+ res = hext_stream;
+ break;
+ }
+
+ if (hstream->opened) {
/*
* check if the stream tag matches the stream
* tag of one of the connected FEs
*/
if (hda_check_fes(rtd, stream_dir,
- stream->stream_tag)) {
- res = hstream;
+ hstream->stream_tag)) {
+ res = hext_stream;
break;
}
} else {
- res = hstream;
+ res = hext_stream;
/*
* This must be a hostless stream.
@@ -119,17 +128,17 @@ static struct hdac_ext_stream *
return res;
}
-static int hda_link_dma_params(struct hdac_ext_stream *stream,
+static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
struct hda_pipe_params *params)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
unsigned char stream_tag = hstream->stream_tag;
struct hdac_bus *bus = hstream->bus;
struct hdac_ext_link *link;
unsigned int format_val;
- snd_hdac_ext_stream_decouple(bus, stream, true);
- snd_hdac_ext_link_stream_reset(stream);
+ snd_hdac_ext_stream_decouple(bus, hext_stream, true);
+ snd_hdac_ext_link_stream_reset(hext_stream);
format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
params->format,
@@ -138,9 +147,9 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
format_val, params->s_freq, params->ch, params->format);
- snd_hdac_ext_link_stream_setup(stream, format_val);
+ snd_hdac_ext_link_stream_setup(hext_stream, format_val);
- if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
list_for_each_entry(link, &bus->hlink_list, list) {
if (link->index == params->link_index)
snd_hdac_ext_link_set_stream_id(link,
@@ -148,55 +157,24 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
}
}
- stream->link_prepared = 1;
+ hext_stream->link_prepared = 1;
return 0;
}
-/* Update config for the DAI widget */
-static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
- int channel)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
-
- if (!swidget)
- return NULL;
-
- sof_dai = swidget->private;
-
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
- return NULL;
- }
-
- config = &sof_dai->dai_config[sof_dai->current_config];
-
- /* update config with stream tag */
- config->hda.link_dma_ch = channel;
-
- return config;
-}
-
static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w,
int channel, bool widget_setup)
{
- struct snd_sof_dev *sdev = hda_stream->sdev;
- struct sof_ipc_dai_config *config;
+ struct snd_sof_dai_config_data data;
- config = hda_dai_update_config(w, channel);
- if (!config) {
- dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
- return -ENOENT;
- }
+ data.dai_data = channel;
/* set up/free DAI widget and send DAI_CONFIG IPC */
if (widget_setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP);
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}
static int hda_link_hw_params(struct snd_pcm_substream *substream,
@@ -205,7 +183,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
{
struct hdac_stream *hstream = substream->runtime->private_data;
struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_stream *link_dev;
+ struct hdac_ext_stream *hext_stream;
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct sof_intel_hda_stream *hda_stream;
@@ -216,18 +194,18 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
int ret;
/* get stored dma data if resuming from system suspend */
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
- if (!link_dev) {
- link_dev = hda_link_stream_assign(bus, substream);
- if (!link_dev)
+ hext_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!hext_stream) {
+ hext_stream = hda_link_stream_assign(bus, substream);
+ if (!hext_stream)
return -EBUSY;
- snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
+ snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream);
}
- stream_tag = hdac_stream(link_dev)->stream_tag;
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
@@ -244,7 +222,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* set the hdac_stream in the codec dai */
- snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
+ snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
p_params.ch = params_channels(params);
@@ -258,20 +236,20 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
else
p_params.link_bps = codec_dai->driver->capture.sig_bits;
- return hda_link_dma_params(link_dev, &p_params);
+ return hda_link_dma_params(hext_stream, &p_params);
}
static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *link_dev =
+ 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_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
int stream = substream->stream;
- if (link_dev->link_prepared)
+ if (hext_stream->link_prepared)
return 0;
dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
@@ -285,35 +263,23 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret;
-
- sof_dai = swidget->private;
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ int ret = 0;
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(sdev->dev, "No config for DAI %s\n", w->name);
- return -EINVAL;
+ if (tplg_ops->dai_config) {
+ ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
+ w->name);
}
- config = &sof_dai->dai_config[sof_dai->current_config];
-
- /* set PAUSE command flag */
- config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);
-
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
- &reply, sizeof(reply));
- if (ret < 0)
- dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);
-
return ret;
}
static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *link_dev =
+ struct hdac_ext_stream *hext_stream =
snd_soc_dai_get_dma_data(dai, substream);
struct sof_intel_hda_stream *hda_stream;
struct snd_soc_pcm_runtime *rtd;
@@ -332,7 +298,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
if (!link)
return -EINVAL;
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
@@ -341,11 +307,11 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_link_stream_start(link_dev);
+ snd_hdac_ext_link_stream_start(hext_stream);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- snd_hdac_ext_link_stream_clear(link_dev);
+ snd_hdac_ext_link_stream_clear(hext_stream);
/*
* free DAI widget during stop/suspend to keep widget use_count's balanced.
@@ -355,14 +321,14 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag);
}
- link_dev->link_prepared = 0;
+ hext_stream->link_prepared = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_link_stream_clear(link_dev);
+ snd_hdac_ext_link_stream_clear(hext_stream);
ret = hda_link_dai_config_pause_push_ipc(w);
if (ret < 0)
@@ -383,22 +349,22 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct hdac_ext_link *link;
struct hdac_stream *hstream;
struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *link_dev;
+ struct hdac_ext_stream *hext_stream;
struct snd_soc_dapm_widget *w;
int ret;
hstream = substream->runtime->private_data;
bus = hstream->bus;
rtd = asoc_substream_to_rtd(substream);
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ hext_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!link_dev) {
+ if (!hext_stream) {
dev_dbg(dai->dev,
- "%s: link_dev is not assigned\n", __func__);
+ "%s: hext_stream is not assigned\n", __func__);
return -EINVAL;
}
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
@@ -415,13 +381,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
return -EINVAL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag);
}
snd_soc_dai_set_dma_data(dai, substream, NULL);
- snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
- link_dev->link_prepared = 0;
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ hext_stream->link_prepared = 0;
/* free the host DMA channel reserved by hostless streams */
hda_stream->host_reserved = 0;
@@ -446,30 +412,17 @@ struct ssp_dai_dma_data {
static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
bool setup)
{
- struct snd_soc_component *component;
- struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
- struct sof_ipc_fw_version *v;
- struct snd_sof_dev *sdev;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
- swidget = w->dobj.private;
- component = swidget->scomp;
- sdev = snd_soc_component_get_drvdata(component);
- v = &sdev->fw_ready.version;
-
- /* DAI_CONFIG IPC during hw_params is not supported in older firmware */
- if (v->abi_version < SOF_ABI_VER(3, 18, 0))
- return 0;
-
if (setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
}
static int ssp_dai_startup(struct snd_pcm_substream *substream,
@@ -724,20 +677,5 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 16,
},
},
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-{
- .name = "Probe Extraction CPU DAI",
- .compress_new = snd_soc_new_compress,
- .cops = &sof_probe_compr_ops,
- .capture = {
- .stream_name = "Probe Extraction",
- .channels_min = 1,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- },
-},
-#endif
#endif
};
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 916a257ea96b..8ddde60c56b3 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -498,15 +498,9 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
case SOF_DSP_PM_D2:
dev_dbg(sdev->dev, "Current DSP power state: D2\n");
break;
- case SOF_DSP_PM_D3_HOT:
- dev_dbg(sdev->dev, "Current DSP power state: D3_HOT\n");
- break;
case SOF_DSP_PM_D3:
dev_dbg(sdev->dev, "Current DSP power state: D3\n");
break;
- case SOF_DSP_PM_D3_COLD:
- dev_dbg(sdev->dev, "Current DSP power state: D3_COLD\n");
- break;
default:
dev_dbg(sdev->dev, "Unknown DSP power state: %d\n",
sdev->dsp_power_state.state);
@@ -904,7 +898,7 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_ext_link *link;
struct hdac_stream *s;
const char *name;
@@ -912,7 +906,7 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
/* set internal flag for BE */
list_for_each_entry(s, &bus->stream_list, list) {
- stream = stream_to_hdac_ext_stream(s);
+ hext_stream = stream_to_hdac_ext_stream(s);
/*
* clear stream. This should already be taken care for running
@@ -920,20 +914,20 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
* streams do not get suspended, so this needs to be done
* explicitly during suspend.
*/
- if (stream->link_substream) {
- rtd = asoc_substream_to_rtd(stream->link_substream);
+ if (hext_stream->link_substream) {
+ rtd = asoc_substream_to_rtd(hext_stream->link_substream);
name = asoc_rtd_to_codec(rtd, 0)->component->name;
link = snd_hdac_ext_bus_get_link(bus, name);
if (!link)
return -EINVAL;
- stream->link_prepared = 0;
+ hext_stream->link_prepared = 0;
- if (hdac_stream(stream)->direction ==
+ if (hdac_stream(hext_stream)->direction ==
SNDRV_PCM_STREAM_CAPTURE)
continue;
- stream_tag = hdac_stream(stream)->stream_tag;
+ stream_tag = hdac_stream(hext_stream)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag);
}
}
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index f0cf8019d72d..0395638c43ae 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -255,39 +255,37 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev,
hda_stream = container_of(hstream,
struct sof_intel_hda_stream,
- hda_stream.hstream);
+ hext_stream.hstream);
/* The stream might already be closed */
if (!hstream)
return -ESTRPIPE;
- sof_mailbox_read(sdev, hda_stream->stream.posn_offset, p, sz);
+ sof_mailbox_read(sdev, hda_stream->sof_intel_stream.posn_offset, p, sz);
}
return 0;
}
-int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
+int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ size_t posn_offset)
{
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
- /* validate offset */
- size_t posn_offset = reply->posn_offset;
hda_stream = container_of(hstream, struct sof_intel_hda_stream,
- hda_stream.hstream);
+ hext_stream.hstream);
/* check for unaligned offset or overflow */
if (posn_offset > sdev->stream_box.size ||
posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
return -EINVAL;
- hda_stream->stream.posn_offset = sdev->stream_box.offset + posn_offset;
+ hda_stream->sof_intel_stream.posn_offset = sdev->stream_box.offset + posn_offset;
dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
- substream->stream, hda_stream->stream.posn_offset);
+ substream->stream, hda_stream->sof_intel_stream.posn_offset);
return 0;
}
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 9bbfdab8009d..2ac5d9d0719b 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -21,26 +21,44 @@
#include <sound/sof.h>
#include "ext_manifest.h"
#include "../ops.h"
+#include "../sof-priv.h"
#include "hda.h"
#define HDA_CL_STREAM_FORMAT 0x40
+static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ int i;
+
+ /* DSP is powered up, set all SSPs to clock consumer/codec provider mode */
+ for (i = 0; i < chip->ssp_count; i++) {
+ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
+ chip->ssp_base_offset
+ + i * SSP_DEV_MEM_SIZE
+ + SSP_SSC1_OFFSET,
+ SSP_SET_CBP_CFP,
+ SSP_SET_CBP_CFP);
+ }
+}
+
static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format,
unsigned int size, struct snd_dma_buffer *dmab,
int direction)
{
- struct hdac_ext_stream *dsp_stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct pci_dev *pci = to_pci_dev(sdev->dev);
int ret;
- dsp_stream = hda_dsp_stream_get(sdev, direction, 0);
+ hext_stream = hda_dsp_stream_get(sdev, direction, 0);
- if (!dsp_stream) {
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no stream available\n");
return ERR_PTR(-ENODEV);
}
- hstream = &dsp_stream->hstream;
+ hstream = &hext_stream->hstream;
hstream->substream = NULL;
/* allocate DMA buffer */
@@ -55,21 +73,21 @@ static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsig
hstream->bufsize = size;
if (direction == SNDRV_PCM_STREAM_CAPTURE) {
- ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL);
+ ret = hda_dsp_iccmax_stream_hw_params(sdev, hext_stream, dmab, NULL);
if (ret < 0) {
dev_err(sdev->dev, "error: iccmax stream prepare failed: %d\n", ret);
goto out_free;
}
} else {
- ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
if (ret < 0) {
dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
goto out_free;
}
- hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size);
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, size);
}
- return dsp_stream;
+ return hext_stream;
out_free:
snd_dma_free_pages(dmab);
@@ -92,7 +110,6 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
char *dump_msg;
u32 flags, j;
int ret;
- int i;
/* step 1: power up corex */
ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask);
@@ -102,15 +119,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag)
goto err;
}
- /* DSP is powered up, set all SSPs to slave mode */
- for (i = 0; i < chip->ssp_count; i++) {
- snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
- chip->ssp_base_offset
- + i * SSP_DEV_MEM_SIZE
- + SSP_SSC1_OFFSET,
- SSP_SET_SLAVE,
- SSP_SET_SLAVE);
- }
+ hda_ssp_set_cbp_cfp(sdev);
/* step 2: purge FW request */
snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req,
@@ -201,9 +210,9 @@ err:
}
static int cl_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd)
+ struct hdac_ext_stream *hext_stream, int cmd)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
/* code loader is special case that reuses stream ops */
@@ -223,19 +232,19 @@ static int cl_trigger(struct snd_sof_dev *sdev,
hstream->running = true;
return 0;
default:
- return hda_dsp_stream_trigger(sdev, stream, cmd);
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
}
static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
- struct hdac_ext_stream *stream)
+ struct hdac_ext_stream *hext_stream)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int ret = 0;
if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
+ ret = hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
else
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
SOF_HDA_SD_CTL_DMA_START, 0);
@@ -259,12 +268,12 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab,
return ret;
}
-static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream)
+static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
{
unsigned int reg;
int ret, status;
- ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_START);
+ ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START);
if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger start failed\n");
return ret;
@@ -288,7 +297,7 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream)
__func__);
}
- ret = cl_trigger(sdev, stream, SNDRV_PCM_TRIGGER_STOP);
+ ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger stop failed\n");
if (!status)
@@ -346,16 +355,55 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
return ret;
}
+static int hda_dsp_boot_imr(struct snd_sof_dev *sdev)
+{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+ unsigned long mask;
+ u32 j;
+ int ret;
+
+ /* power up & unstall/run the cores to run the firmware */
+ ret = hda_dsp_enable_core(sdev, chip->init_core_mask);
+ if (ret < 0) {
+ dev_err(sdev->dev, "dsp core start failed %d\n", ret);
+ return -EIO;
+ }
+
+ /* set enabled cores mask and increment ref count for cores in init_core_mask */
+ sdev->enabled_cores_mask |= chip->init_core_mask;
+ mask = sdev->enabled_cores_mask;
+ for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES)
+ sdev->dsp_core_ref_count[j]++;
+
+ hda_ssp_set_cbp_cfp(sdev);
+
+ /* enable IPC interrupts */
+ hda_dsp_ipc_int_enable(sdev);
+
+ /* process wakes */
+ hda_sdw_process_wakeen(sdev);
+
+ return ret;
+}
+
int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_sof_pdata *plat_data = sdev->pdata;
const struct sof_dev_desc *desc = plat_data->desc;
const struct sof_intel_dsp_desc *chip_info;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct firmware stripped_firmware;
int ret, ret1, i;
+ if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) &&
+ !(sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) &&
+ !sdev->first_boot) {
+ dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n");
+ return hda_dsp_boot_imr(sdev);
+ }
+
chip_info = desc->chip_info;
if (plat_data->fw->size <= plat_data->fw_offset) {
@@ -370,11 +418,11 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
init_waitqueue_head(&sdev->boot_wait);
/* prepare DMA for code loader stream */
- stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
- &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK);
- if (IS_ERR(stream)) {
+ hext_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size,
+ &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK);
+ if (IS_ERR(hext_stream)) {
dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
- return PTR_ERR(stream);
+ return PTR_ERR(hext_stream);
}
memcpy(sdev->dmab.area, stripped_firmware.data,
@@ -386,7 +434,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
"Attempting iteration %d of Core En/ROM load...\n", i);
hda->boot_iteration = i + 1;
- ret = cl_dsp_init(sdev, stream->hstream.stream_tag);
+ ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag);
/* don't retry anymore if successful */
if (!ret)
@@ -425,7 +473,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
* Continue with code loading and firmware boot
*/
hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS;
- ret = cl_copy_fw(sdev, stream);
+ ret = cl_copy_fw(sdev, hext_stream);
if (!ret)
dev_dbg(sdev->dev, "Firmware download successful, booting...\n");
else
@@ -438,7 +486,7 @@ cleanup:
* This should be done even if firmware loading fails.
* If the cleanup also fails, we return the initial error
*/
- ret1 = cl_cleanup(sdev, &sdev->dmab, stream);
+ ret1 = cl_cleanup(sdev, &sdev->dmab, hext_stream);
if (ret1 < 0) {
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index d78aa5d8552d..dc1f743730c0 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -93,13 +93,12 @@ u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits)
int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params)
+ struct snd_sof_platform_stream_params *platform_params)
{
struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_dma_buffer *dmab;
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
int ret;
u32 size, rate, bits;
@@ -118,7 +117,7 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
(params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, params);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, params);
if (ret < 0) {
dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
return ret;
@@ -126,23 +125,14 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
/* enable SPIB when rewinds are disabled */
if (hda_disable_rewinds)
- hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0);
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_ENABLE, 0);
else
- hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0);
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
- /* update no_stream_position flag for ipc params */
- if (hda && hda->no_ipc_position) {
- /* For older ABIs set host_period_bytes to zero to inform
- * FW we don't want position updates. Newer versions use
- * no_stream_position for this purpose.
- */
- if (v->abi_version < SOF_ABI_VER(3, 10, 0))
- ipc_params->host_period_bytes = 0;
- else
- ipc_params->no_stream_position = 1;
- }
+ if (hda)
+ platform_params->no_ipc_position = hda->no_ipc_position;
- ipc_params->stream_tag = hstream->stream_tag;
+ platform_params->stream_tag = hstream->stream_tag;
return 0;
}
@@ -174,9 +164,9 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream, int cmd)
{
struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_ext_stream *stream = stream_to_hdac_ext_stream(hstream);
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
- return hda_dsp_stream_trigger(sdev, stream, cmd);
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev,
@@ -315,6 +305,7 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
if (hda_always_enable_dmi_l1 ||
+ direction == SNDRV_PCM_STREAM_PLAYBACK ||
spcm->stream[substream->stream].d0i3_compatible)
flags |= SOF_HDA_STREAM_DMI_L1_COMPATIBLE;
diff --git a/sound/soc/sof/intel/hda-probes.c b/sound/soc/sof/intel/hda-probes.c
index fe2f3f7d236b..31e85d4aae8c 100644
--- a/sound/soc/sof/intel/hda-probes.c
+++ b/sound/soc/sof/intel/hda-probes.c
@@ -3,14 +3,20 @@
// 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) 2019-2020 Intel Corporation. All rights reserved.
+// Copyright(c) 2019-2021 Intel Corporation. All rights reserved.
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+// Converted to SOF client:
+// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
//
+#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include <sound/soc.h>
#include "../sof-priv.h"
+#include "../sof-client-probes.h"
+#include "../sof-client.h"
#include "hda.h"
static inline struct hdac_ext_stream *
@@ -19,50 +25,55 @@ hda_compr_get_stream(struct snd_compr_stream *cstream)
return cstream->runtime->private_data;
}
-int hda_probe_compr_assign(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
+static int hda_probes_compr_assign(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai, u32 *stream_id)
{
- struct hdac_ext_stream *stream;
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct hdac_ext_stream *hext_stream;
- stream = hda_dsp_stream_get(sdev, cstream->direction, 0);
- if (!stream)
+ hext_stream = hda_dsp_stream_get(sdev, cstream->direction, 0);
+ if (!hext_stream)
return -EBUSY;
- hdac_stream(stream)->curr_pos = 0;
- hdac_stream(stream)->cstream = cstream;
- cstream->runtime->private_data = stream;
+ hdac_stream(hext_stream)->curr_pos = 0;
+ hdac_stream(hext_stream)->cstream = cstream;
+ cstream->runtime->private_data = hext_stream;
- return hdac_stream(stream)->stream_tag;
+ *stream_id = hdac_stream(hext_stream)->stream_tag;
+
+ return 0;
}
-int hda_probe_compr_free(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
+static int hda_probes_compr_free(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
int ret;
ret = hda_dsp_stream_put(sdev, cstream->direction,
- hdac_stream(stream)->stream_tag);
+ hdac_stream(hext_stream)->stream_tag);
if (ret < 0) {
dev_dbg(sdev->dev, "stream put failed: %d\n", ret);
return ret;
}
- hdac_stream(stream)->cstream = NULL;
+ hdac_stream(hext_stream)->cstream = NULL;
cstream->runtime->private_data = NULL;
return 0;
}
-int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai)
+static int hda_probes_compr_set_params(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
- struct hdac_stream *hstream = hdac_stream(stream);
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct hdac_stream *hstream = hdac_stream(hext_stream);
struct snd_dma_buffer *dmab;
u32 bits, rate;
int bps, ret;
@@ -80,7 +91,7 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
hstream->period_bytes = cstream->runtime->fragment_size;
hstream->no_period_wakeup = 0;
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
if (ret < 0) {
dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
return ret;
@@ -89,26 +100,49 @@ int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
return 0;
}
-int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai)
+static int hda_probes_compr_trigger(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ int cmd, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
- return hda_dsp_stream_trigger(sdev, stream, cmd);
+ return hda_dsp_stream_trigger(sdev, hext_stream, cmd);
}
-int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai)
+static int hda_probes_compr_pointer(struct sof_client_dev *cdev,
+ struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *stream = hda_compr_get_stream(cstream);
+ struct hdac_ext_stream *hext_stream = hda_compr_get_stream(cstream);
struct snd_soc_pcm_stream *pstream;
pstream = &dai->driver->capture;
- tstamp->copied_total = hdac_stream(stream)->curr_pos;
+ tstamp->copied_total = hdac_stream(hext_stream)->curr_pos;
tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
return 0;
}
+
+/* SOF client implementation */
+static const struct sof_probes_host_ops hda_probes_ops = {
+ .assign = hda_probes_compr_assign,
+ .free = hda_probes_compr_free,
+ .set_params = hda_probes_compr_set_params,
+ .trigger = hda_probes_compr_trigger,
+ .pointer = hda_probes_compr_pointer,
+};
+
+int hda_probes_register(struct snd_sof_dev *sdev)
+{
+ return sof_client_dev_register(sdev, "hda-probes", 0, &hda_probes_ops,
+ sizeof(hda_probes_ops));
+}
+
+void hda_probes_unregister(struct snd_sof_dev *sdev)
+{
+ sof_client_dev_unregister(sdev, "hda-probes", 0);
+}
+
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index ba60807fbd8f..daeb64c495e4 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -57,7 +57,7 @@ static char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream)
*/
static int hda_setup_bdle(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream,
+ struct hdac_stream *hstream,
struct sof_intel_dsp_bdl **bdlp,
int offset, int size, int ioc)
{
@@ -68,7 +68,7 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
dma_addr_t addr;
int chunk;
- if (stream->frags >= HDA_DSP_MAX_BDL_ENTRIES) {
+ if (hstream->frags >= HDA_DSP_MAX_BDL_ENTRIES) {
dev_err(sdev->dev, "error: stream frags exceeded\n");
return -EINVAL;
}
@@ -91,11 +91,11 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
size -= chunk;
bdl->ioc = (size || !ioc) ? 0 : cpu_to_le32(0x01);
bdl++;
- stream->frags++;
+ hstream->frags++;
offset += chunk;
dev_vdbg(sdev->dev, "bdl, frags:%d, chunk size:0x%x;\n",
- stream->frags, chunk);
+ hstream->frags, chunk);
}
*bdlp = bdl;
@@ -108,47 +108,47 @@ static int hda_setup_bdle(struct snd_sof_dev *sdev,
*/
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream)
+ struct hdac_stream *hstream)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct sof_intel_dsp_bdl *bdl;
int i, offset, period_bytes, periods;
int remain, ioc;
- period_bytes = stream->period_bytes;
+ period_bytes = hstream->period_bytes;
dev_dbg(sdev->dev, "%s: period_bytes:0x%x\n", __func__, period_bytes);
if (!period_bytes)
- period_bytes = stream->bufsize;
+ period_bytes = hstream->bufsize;
- periods = stream->bufsize / period_bytes;
+ periods = hstream->bufsize / period_bytes;
dev_dbg(sdev->dev, "%s: periods:%d\n", __func__, periods);
- remain = stream->bufsize % period_bytes;
+ remain = hstream->bufsize % period_bytes;
if (remain)
periods++;
/* program the initial BDL entries */
- bdl = (struct sof_intel_dsp_bdl *)stream->bdl.area;
+ bdl = (struct sof_intel_dsp_bdl *)hstream->bdl.area;
offset = 0;
- stream->frags = 0;
+ hstream->frags = 0;
/*
* set IOC if don't use position IPC
* and period_wakeup needed.
*/
ioc = hda->no_ipc_position ?
- !stream->no_period_wakeup : 0;
+ !hstream->no_period_wakeup : 0;
for (i = 0; i < periods; i++) {
if (i == (periods - 1) && remain)
/* set the last small entry */
offset = hda_setup_bdle(sdev, dmab,
- stream, &bdl, offset,
+ hstream, &bdl, offset,
remain, 0);
else
offset = hda_setup_bdle(sdev, dmab,
- stream, &bdl, offset,
+ hstream, &bdl, offset,
period_bytes, ioc);
}
@@ -156,10 +156,10 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
}
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
int enable, u32 size)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
u32 mask;
if (!sdev->bar[HDA_DSP_SPIB_BAR]) {
@@ -175,7 +175,7 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
enable << hstream->index);
/* set the SPIB value */
- sof_io_write(sdev, stream->spib_addr, size);
+ sof_io_write(sdev, hext_stream->spib_addr, size);
return 0;
}
@@ -186,7 +186,7 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
- struct hdac_ext_stream *stream = NULL;
+ struct hdac_ext_stream *hext_stream = NULL;
struct hdac_stream *s;
spin_lock_irq(&bus->reg_lock);
@@ -194,10 +194,10 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
/* get an unused stream */
list_for_each_entry(s, &bus->stream_list, list) {
if (s->direction == direction && !s->opened) {
- stream = stream_to_hdac_ext_stream(s);
- hda_stream = container_of(stream,
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream,
struct sof_intel_hda_stream,
- hda_stream);
+ hext_stream);
/* check if the host DMA channel is reserved */
if (hda_stream->host_reserved)
continue;
@@ -210,11 +210,11 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
spin_unlock_irq(&bus->reg_lock);
/* stream found ? */
- if (!stream) {
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no free %s streams\n",
direction == SNDRV_PCM_STREAM_PLAYBACK ?
"playback" : "capture");
- return stream;
+ return hext_stream;
}
hda_stream->flags = flags;
@@ -229,7 +229,7 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
- return stream;
+ return hext_stream;
}
/* free a stream */
@@ -237,7 +237,7 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *s;
bool dmi_l1_enable = true;
bool found = false;
@@ -249,8 +249,8 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
* that are DMI L1 incompatible.
*/
list_for_each_entry(s, &bus->stream_list, list) {
- stream = stream_to_hdac_ext_stream(s);
- hda_stream = container_of(stream, struct sof_intel_hda_stream, hda_stream);
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, hext_stream);
if (!s->opened)
continue;
@@ -319,9 +319,9 @@ static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hs
}
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd)
+ struct hdac_ext_stream *hext_stream, int cmd)
{
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
int ret = 0;
@@ -396,17 +396,17 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
}
/* minimal recommended programming for ICCMAX stream */
-int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream,
+int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params)
{
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int ret;
u32 mask = 0x1 << hstream->index;
- if (!stream) {
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no stream available\n");
return -ENODEV;
}
@@ -467,20 +467,20 @@ int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_st
* and normal stream.
*/
int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params)
{
const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *hstream = &stream->hstream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
int ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
u32 mask;
u32 run;
- if (!stream) {
+ if (!hext_stream) {
dev_err(sdev->dev, "error: no stream available\n");
return -ENODEV;
}
@@ -659,28 +659,28 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
{
- struct hdac_stream *stream = substream->runtime->private_data;
- struct hdac_ext_stream *link_dev = container_of(stream,
- struct hdac_ext_stream,
- hstream);
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
struct hdac_bus *bus = sof_to_bus(sdev);
- u32 mask = 0x1 << stream->index;
+ u32 mask = 0x1 << hstream->index;
int ret;
- ret = hda_dsp_stream_reset(sdev, stream);
+ ret = hda_dsp_stream_reset(sdev, hstream);
if (ret < 0)
return ret;
spin_lock_irq(&bus->reg_lock);
/* couple host and link DMA if link DMA channel is idle */
- if (!link_dev->link_locked)
+ if (!hext_stream->link_locked)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
SOF_HDA_REG_PP_PPCTL, mask, 0);
spin_unlock_irq(&bus->reg_lock);
- hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0);
+ hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
- stream->substream = NULL;
+ hstream->substream = NULL;
return 0;
}
@@ -808,7 +808,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
int hda_dsp_stream_init(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
@@ -872,27 +872,27 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hda_stream->sdev = sdev;
- stream = &hda_stream->hda_stream;
+ hext_stream = &hda_stream->hext_stream;
- stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
/* do we support SPIB */
if (sdev->bar[HDA_DSP_SPIB_BAR]) {
- stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hext_stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_SPIB;
- stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hext_stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_MAXFIFO;
}
- hstream = &stream->hstream;
+ hstream = &hext_stream->hstream;
hstream->bus = bus;
hstream->sd_int_sta_mask = 1 << i;
hstream->index = i;
@@ -927,28 +927,28 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hda_stream->sdev = sdev;
- stream = &hda_stream->hda_stream;
+ hext_stream = &hda_stream->hext_stream;
/* we always have DSP support */
- stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i;
/* do we support SPIB */
if (sdev->bar[HDA_DSP_SPIB_BAR]) {
- stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hext_stream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_SPIB;
- stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
+ hext_stream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] +
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_MAXFIFO;
}
- hstream = &stream->hstream;
+ hstream = &hext_stream->hstream;
hstream->bus = bus;
hstream->sd_int_sta_mask = 1 << i;
hstream->index = i;
@@ -983,7 +983,7 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
struct hdac_stream *s, *_s;
- struct hdac_ext_stream *stream;
+ struct hdac_ext_stream *hext_stream;
struct sof_intel_hda_stream *hda_stream;
/* free position buffer */
@@ -1003,9 +1003,9 @@ void hda_dsp_stream_free(struct snd_sof_dev *sdev)
if (s->bdl.area)
snd_dma_free_pages(&s->bdl);
list_del(&s->list);
- stream = stream_to_hdac_ext_stream(s);
- hda_stream = container_of(stream, struct sof_intel_hda_stream,
- hda_stream);
+ hext_stream = stream_to_hdac_ext_stream(s);
+ hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
+ hext_stream);
devm_kfree(sdev->dev, hda_stream);
}
}
diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c
index 29e3da3c63db..755ef1d835e0 100644
--- a/sound/soc/sof/intel/hda-trace.c
+++ b/sound/soc/sof/intel/hda-trace.c
@@ -19,25 +19,25 @@
#include "../ops.h"
#include "hda.h"
-static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev)
+static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
- struct hdac_ext_stream *stream = hda->dtrace_stream;
- struct hdac_stream *hstream = &stream->hstream;
- struct snd_dma_buffer *dmab = &sdev->dmatb;
+ struct hdac_ext_stream *hext_stream = hda->dtrace_stream;
+ struct hdac_stream *hstream = &hext_stream->hstream;
int ret;
hstream->period_bytes = 0;/* initialize period_bytes */
- hstream->bufsize = sdev->dmatb.bytes;
+ hstream->bufsize = dmab->bytes;
- ret = hda_dsp_stream_hw_params(sdev, stream, dmab, NULL);
+ ret = hda_dsp_stream_hw_params(sdev, hext_stream, dmab, NULL);
if (ret < 0)
dev_err(sdev->dev, "error: hdac prepare failed: %d\n", ret);
return ret;
}
-int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
+int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
int ret;
@@ -51,18 +51,19 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag)
return -ENODEV;
}
- *stream_tag = hda->dtrace_stream->hstream.stream_tag;
+ dtrace_params->stream_tag = hda->dtrace_stream->hstream.stream_tag;
/*
* initialize capture stream, set BDL address and return corresponding
* stream tag which will be sent to the firmware by IPC message.
*/
- ret = hda_dsp_trace_prepare(sdev);
+ ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb);
if (ret < 0) {
dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret);
- hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, *stream_tag);
+ hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE,
+ dtrace_params->stream_tag);
hda->dtrace_stream = NULL;
- *stream_tag = 0;
+ dtrace_params->stream_tag = 0;
}
return ret;
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 1385695d7745..9c97c80a7f48 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -41,100 +41,68 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
+ struct snd_sof_dai_config_data *data)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ struct snd_sof_dai *sof_dai = swidget->private;
int ret;
- sof_dai = swidget->private;
-
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(sdev->dev, "No config for DAI %s\n", w->name);
+ if (!sof_dai) {
+ dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name);
return -EINVAL;
}
- /* DAI already configured, reset it before reconfiguring it */
- if (sof_dai->configured) {
- ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
- if (ret < 0)
- return ret;
- }
+ if (tplg_ops->dai_config) {
+ unsigned int flags;
- config = &sof_dai->dai_config[sof_dai->current_config];
+ /* set HW_PARAMS flag along with quirks */
+ flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
+ quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- /*
- * For static pipelines, the DAI widget would already be set up and calling
- * sof_widget_setup() simply returns without doing anything.
- * For dynamic pipelines, the DAI widget will be set up now.
- */
- ret = sof_widget_setup(sdev, swidget);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name);
- return ret;
- }
-
- /* set HW_PARAMS flag along with quirks */
- config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
-
-
- /* send DAI_CONFIG IPC */
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
- &reply, sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name);
- return ret;
+ ret = tplg_ops->dai_config(sdev, swidget, flags, data);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
+ w->name);
+ return ret;
+ }
}
- sof_dai->configured = true;
-
return 0;
}
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags)
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
+ struct snd_sof_dai_config_data *data)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
- int ret;
-
- sof_dai = swidget->private;
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ struct snd_sof_dai *sof_dai = swidget->private;
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name);
+ if (!sof_dai) {
+ dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name);
return -EINVAL;
}
- /* nothing to do if hw_free() is called without restarting the stream after resume. */
- if (!sof_dai->configured)
- return 0;
-
- config = &sof_dai->dai_config[sof_dai->current_config];
-
- /* set HW_FREE flag along with any quirks */
- config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ if (tplg_ops->dai_config) {
+ unsigned int flags;
+ int ret;
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
- &reply, sizeof(reply));
- if (ret < 0)
- dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name);
+ /* set HW_FREE flag along with any quirks */
+ flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
+ quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- /*
- * Reset the configured_flag and free the widget even if the IPC fails to keep
- * the widget use_count balanced
- */
- sof_dai->configured = false;
+ ret = tplg_ops->dai_config(sdev, swidget, flags, data);
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__,
+ w->name);
+ }
- return sof_widget_free(sdev, swidget);
+ return 0;
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
@@ -149,62 +117,34 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
module_param(sdw_clock_stop_quirks, int, 0444);
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
-static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
- struct snd_soc_dapm_widget *w,
- int link_id, int alh_stream_id, int dai_id, bool setup)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
-
- if (!swidget) {
- dev_err(sdev->dev, "error: No private data for widget %s\n", w->name);
- return -EINVAL;
- }
-
- sof_dai = swidget->private;
-
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(sdev->dev, "error: No config for DAI %s\n", w->name);
- return -EINVAL;
- }
-
- config = &sof_dai->dai_config[sof_dai->current_config];
-
- /* update config with link and stream ID */
- config->dai_index = (link_id << 8) | dai_id;
- config->alh.stream_id = alh_stream_id;
-
- if (setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
-}
-
static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
+ struct snd_sof_dai_config_data data;
struct snd_soc_dapm_widget *w;
w = snd_soc_dai_get_widget(d, params_data->stream);
+ data.dai_index = (params_data->link_id << 8) | d->id;
+ data.dai_data = params_data->alh_stream_id;
- return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
- d->id, true);
+ return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}
static int sdw_free_stream(struct device *dev,
struct sdw_intel_stream_free_data *free_data)
{
- struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
+ struct snd_sof_dai_config_data data;
struct snd_soc_dapm_widget *w;
w = snd_soc_dai_get_widget(d, free_data->stream);
+ data.dai_index = (free_data->link_id << 8) | d->id;
/* send invalid stream_id */
- return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
+ data.dai_data = 0xFFFF;
+
+ return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}
static const struct sdw_intel_ops sdw_callback = {
@@ -432,11 +372,9 @@ static char *hda_model;
module_param(hda_model, charp, 0444);
MODULE_PARM_DESC(hda_model, "Use the given HDA board model.");
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
-static int hda_dmic_num = -1;
-module_param_named(dmic_num, hda_dmic_num, int, 0444);
+static int dmic_num_override = -1;
+module_param_named(dmic_num, dmic_num_override, int, 0444);
MODULE_PARM_DESC(dmic_num, "SOF HDA DMIC number");
-#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
static bool hda_codec_use_common_hdmi = IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI);
@@ -534,7 +472,7 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *le
void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags)
{
- char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR;
+ char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
struct sof_ipc_dsp_oops_xtensa xoops;
struct sof_ipc_panic_info panic_info;
u32 stack[HDA_DSP_STACK_DUMP_SIZE];
@@ -644,24 +582,54 @@ static int hda_init(struct snd_sof_dev *sdev)
return ret;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
-
-static int check_nhlt_dmic(struct snd_sof_dev *sdev)
+static int check_dmic_num(struct snd_sof_dev *sdev)
{
struct nhlt_acpi_table *nhlt;
- int dmic_num;
+ int dmic_num = 0;
nhlt = intel_nhlt_init(sdev->dev);
if (nhlt) {
dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt);
intel_nhlt_free(nhlt);
- if (dmic_num >= 1 && dmic_num <= 4)
- return dmic_num;
}
- return 0;
+ /* allow for module parameter override */
+ if (dmic_num_override != -1) {
+ dev_dbg(sdev->dev,
+ "overriding DMICs detected in NHLT tables %d by kernel param %d\n",
+ dmic_num, dmic_num_override);
+ dmic_num = dmic_num_override;
+ }
+
+ if (dmic_num < 0 || dmic_num > 4) {
+ dev_dbg(sdev->dev, "invalid dmic_number %d\n", dmic_num);
+ dmic_num = 0;
+ }
+
+ return dmic_num;
}
+static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev)
+{
+ struct nhlt_acpi_table *nhlt;
+ int ssp_mask = 0;
+
+ nhlt = intel_nhlt_init(sdev->dev);
+ if (!nhlt)
+ return ssp_mask;
+
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP)) {
+ ssp_mask = intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S);
+ if (ssp_mask)
+ dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask);
+ }
+ intel_nhlt_free(nhlt);
+
+ return ssp_mask;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) || IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+
static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
const char *sof_tplg_filename,
const char *idisp_str,
@@ -697,16 +665,8 @@ static int dmic_topology_fixup(struct snd_sof_dev *sdev,
const char *dmic_str;
int dmic_num;
- /* first check NHLT for DMICs */
- dmic_num = check_nhlt_dmic(sdev);
-
- /* allow for module parameter override */
- if (hda_dmic_num != -1) {
- dev_dbg(sdev->dev,
- "overriding DMICs detected in NHLT tables %d by kernel param %d\n",
- dmic_num, hda_dmic_num);
- dmic_num = hda_dmic_num;
- }
+ /* first check for DMICs (using NHLT or module parameter) */
+ dmic_num = check_dmic_num(sdev);
switch (dmic_num) {
case 1:
@@ -1180,6 +1140,10 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev,
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+
+#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \
+ SDW_MFG_ID_MASK | SDW_PART_ID_MASK))
+
/* Check if all Slaves defined on the link can be found */
static bool link_slaves_found(struct snd_sof_dev *sdev,
const struct snd_soc_acpi_link_adr *link,
@@ -1188,7 +1152,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev,
struct hdac_bus *bus = sof_to_bus(sdev);
struct sdw_intel_slave_id *ids = sdw->ids;
int num_slaves = sdw->num_slaves;
- unsigned int part_id, link_id, unique_id, mfg_id;
+ unsigned int part_id, link_id, unique_id, mfg_id, version;
int i, j, k;
for (i = 0; i < link->num_adr; i++) {
@@ -1198,12 +1162,14 @@ static bool link_slaves_found(struct snd_sof_dev *sdev,
mfg_id = SDW_MFG_ID(adr);
part_id = SDW_PART_ID(adr);
link_id = SDW_DISCO_LINK_ID(adr);
+ version = SDW_VERSION(adr);
for (j = 0; j < num_slaves; j++) {
/* find out how many identical parts were reported on that link */
if (ids[j].link_id == link_id &&
ids[j].id.part_id == part_id &&
- ids[j].id.mfg_id == mfg_id)
+ ids[j].id.mfg_id == mfg_id &&
+ ids[j].id.sdw_version == version)
reported_part_count++;
}
@@ -1212,21 +1178,15 @@ static bool link_slaves_found(struct snd_sof_dev *sdev,
if (ids[j].link_id != link_id ||
ids[j].id.part_id != part_id ||
- ids[j].id.mfg_id != mfg_id)
+ ids[j].id.mfg_id != mfg_id ||
+ ids[j].id.sdw_version != version)
continue;
/* find out how many identical parts are expected */
for (k = 0; k < link->num_adr; k++) {
u64 adr2 = link->adr_d[k].adr;
- unsigned int part_id2, link_id2, mfg_id2;
-
- mfg_id2 = SDW_MFG_ID(adr2);
- part_id2 = SDW_PART_ID(adr2);
- link_id2 = SDW_DISCO_LINK_ID(adr2);
- if (link_id2 == link_id &&
- part_id2 == part_id &&
- mfg_id2 == mfg_id)
+ if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr))
expected_part_count++;
}
@@ -1314,10 +1274,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
mach->mach_params.platform = dev_name(sdev->dev);
- if (mach->sof_fw_filename)
- pdata->fw_filename = mach->sof_fw_filename;
- else
- pdata->fw_filename = pdata->desc->default_fw_filename;
+ pdata->fw_filename = pdata->desc->default_fw_filename;
pdata->tplg_filename = mach->sof_tplg_filename;
/*
@@ -1377,9 +1334,12 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_soc_acpi_mach *mach;
+ const char *tplg_filename;
mach = snd_soc_acpi_find_machine(desc->machines);
if (mach) {
+ bool add_extension = false;
+
/*
* If tplg file name is overridden, use it instead of
* the one set in mach table
@@ -1387,10 +1347,65 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
if (!sof_pdata->tplg_filename)
sof_pdata->tplg_filename = mach->sof_tplg_filename;
+ /* report to machine driver if any DMICs are found */
+ mach->mach_params.dmic_num = check_dmic_num(sdev);
+
+ if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER &&
+ mach->mach_params.dmic_num) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s%d%s",
+ sof_pdata->tplg_filename,
+ "-dmic",
+ mach->mach_params.dmic_num,
+ "ch");
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ add_extension = true;
+ }
+
if (mach->link_mask) {
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
}
+
+ /* report SSP link mask to machine driver */
+ mach->mach_params.i2s_link_mask = check_nhlt_ssp_mask(sdev);
+
+ if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER &&
+ mach->mach_params.i2s_link_mask) {
+ int ssp_num;
+
+ if (hweight_long(mach->mach_params.i2s_link_mask) > 1 &&
+ !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB))
+ dev_warn(sdev->dev, "More than one SSP exposed by NHLT, choosing MSB\n");
+
+ /* fls returns 1-based results, SSPs indices are 0-based */
+ ssp_num = fls(mach->mach_params.i2s_link_mask) - 1;
+
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s%d",
+ sof_pdata->tplg_filename,
+ "-ssp",
+ ssp_num);
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ add_extension = true;
+ }
+
+ if (add_extension) {
+ tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s%s",
+ sof_pdata->tplg_filename,
+ ".tplg");
+ if (!tplg_filename)
+ return NULL;
+
+ sof_pdata->tplg_filename = tplg_filename;
+ }
}
/*
@@ -1424,6 +1439,16 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
}
EXPORT_SYMBOL_NS(hda_pci_intel_probe, SND_SOC_SOF_INTEL_HDA_COMMON);
+int hda_register_clients(struct snd_sof_dev *sdev)
+{
+ return hda_probes_register(sdev);
+}
+
+void hda_unregister_clients(struct snd_sof_dev *sdev)
+{
+ hda_probes_unregister(sdev);
+}
+
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);
MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 03a6bb7a165c..05e5e158614a 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -16,6 +16,8 @@
#include <sound/compress_driver.h>
#include <sound/hda_codec.h>
#include <sound/hdaudio_ext.h>
+#include "../sof-client-probes.h"
+#include "../sof-audio.h"
#include "shim.h"
/* PCI registers */
@@ -351,13 +353,7 @@
/* Number of DAIs */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-#define SOF_SKL_NUM_DAIS 16
-#else
#define SOF_SKL_NUM_DAIS 15
-#endif
-
#else
#define SOF_SKL_NUM_DAIS 8
#endif
@@ -383,9 +379,9 @@
/* SSP Registers */
#define SSP_SSC1_OFFSET 0x4
-#define SSP_SET_SCLK_SLAVE BIT(25)
-#define SSP_SET_SFRM_SLAVE BIT(24)
-#define SSP_SET_SLAVE (SSP_SET_SCLK_SLAVE | SSP_SET_SFRM_SLAVE)
+#define SSP_SET_SCLK_CONSUMER BIT(25)
+#define SSP_SET_SFRM_CONSUMER BIT(24)
+#define SSP_SET_CBP_CFP (SSP_SET_SCLK_CONSUMER | SSP_SET_SFRM_CONSUMER)
#define HDA_IDISP_ADDR 2
#define HDA_IDISP_CODEC(x) ((x) & BIT(HDA_IDISP_ADDR))
@@ -471,14 +467,14 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s)
struct sof_intel_hda_stream {
struct snd_sof_dev *sdev;
- struct hdac_ext_stream hda_stream;
- struct sof_intel_stream stream;
+ struct hdac_ext_stream hext_stream;
+ struct sof_intel_stream sof_intel_stream;
int host_reserved; /* reserve host DMA channel */
u32 flags;
};
#define hstream_to_sof_hda_stream(hstream) \
- container_of(hstream, struct sof_intel_hda_stream, hda_stream)
+ container_of(hstream, struct sof_intel_hda_stream, hext_stream)
#define bus_to_sof_hda(bus) \
container_of(bus, struct sof_intel_hda_dev, hbus.core)
@@ -529,7 +525,7 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev,
int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params);
+ struct snd_sof_platform_stream_params *platform_params);
int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev,
@@ -545,18 +541,19 @@ int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substrea
int hda_dsp_stream_init(struct snd_sof_dev *sdev);
void hda_dsp_stream_free(struct snd_sof_dev *sdev);
int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params);
-int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream,
+int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev,
+ struct hdac_ext_stream *hext_stream,
struct snd_dma_buffer *dmab,
struct snd_pcm_hw_params *params);
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream, int cmd);
+ struct hdac_ext_stream *hext_stream, int cmd);
irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context);
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
- struct hdac_stream *stream);
+ struct hdac_stream *hstream);
bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev);
bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev);
@@ -564,38 +561,15 @@ struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags);
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag);
int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
- struct hdac_ext_stream *stream,
+ struct hdac_ext_stream *hext_stream,
int enable, u32 size);
int hda_ipc_msg_data(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
void *p, size_t sz);
-int hda_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply);
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-/*
- * Probe Compress Operations.
- */
-int hda_probe_compr_assign(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int hda_probe_compr_free(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai);
-int hda_probe_compr_set_params(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai);
-int hda_probe_compr_trigger(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai);
-int hda_probe_compr_pointer(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai);
-#endif
+int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ size_t posn_offset);
/*
* DSP IPC Operations.
@@ -670,7 +644,8 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
/*
* Trace Control.
*/
-int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag);
+int hda_dsp_trace_init(struct snd_sof_dev *sdev,
+ struct sof_ipc_dma_trace_params_ext *dtrace_params);
int hda_dsp_trace_release(struct snd_sof_dev *sdev);
int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd);
@@ -727,6 +702,25 @@ extern const struct sof_intel_dsp_desc ehl_chip_info;
extern const struct sof_intel_dsp_desc jsl_chip_info;
extern const struct sof_intel_dsp_desc adls_chip_info;
+/* Probes support */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
+int hda_probes_register(struct snd_sof_dev *sdev);
+void hda_probes_unregister(struct snd_sof_dev *sdev);
+#else
+static inline int hda_probes_register(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void hda_probes_unregister(struct snd_sof_dev *sdev)
+{
+}
+#endif /* CONFIG_SND_SOC_SOF_HDA_PROBES */
+
+/* SOF client registration for HDA platforms */
+int hda_register_clients(struct snd_sof_dev *sdev);
+void hda_unregister_clients(struct snd_sof_dev *sdev);
+
/* machine driver select */
struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev);
void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
@@ -737,8 +731,10 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_sof_dai;
struct sof_ipc_dai_config;
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags);
+int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
+ struct snd_sof_dai_config_data *data);
+int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
+ struct snd_sof_dai_config_data *data);
#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index f75e3983969f..b44a649bfc0b 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -118,7 +118,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.get_window_offset = hda_dsp_ipc_get_window_offset,
.ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
+ .set_stream_data_offset = hda_set_stream_data_offset,
/* machine driver */
.machine_select = hda_machine_select,
@@ -142,15 +142,6 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
@@ -173,6 +164,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index fd46210f1730..feaec251adc8 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -110,6 +110,8 @@ static const struct pci_device_id sof_pci_ids[] = {
.driver_data = (unsigned long)&ehl_desc},
{ PCI_DEVICE(0x8086, 0x7ad0), /* ADL-S */
.driver_data = (unsigned long)&adls_desc},
+ { PCI_DEVICE(0x8086, 0x7a50), /* RPL-S */
+ .driver_data = (unsigned long)&adls_desc},
{ PCI_DEVICE(0x8086, 0x51c8), /* ADL-P */
.driver_data = (unsigned long)&adl_desc},
{ PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */
diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c
index f8c841caa362..6efef225973f 100644
--- a/sound/soc/sof/intel/pci-tng.c
+++ b/sound/soc/sof/intel/pci-tng.c
@@ -25,7 +25,6 @@ static struct snd_soc_acpi_mach sof_tng_machines[] = {
{
.id = "INT343A",
.drv_name = "edison",
- .sof_fw_filename = "sof-byt.ri",
.sof_tplg_filename = "sof-byt.tplg",
},
{}
@@ -166,7 +165,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.get_window_offset = atom_get_window_offset,
.ipc_msg_data = sof_ipc_msg_data,
- .ipc_pcm_params = sof_ipc_pcm_params,
+ .set_stream_data_offset = sof_set_stream_data_offset,
/* machine driver */
.machine_select = atom_machine_select,
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 7f7929c5cb88..cb1c319d5bee 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -91,7 +91,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.get_window_offset = hda_dsp_ipc_get_window_offset,
.ipc_msg_data = hda_ipc_msg_data,
- .ipc_pcm_params = hda_ipc_pcm_params,
+ .set_stream_data_offset = hda_set_stream_data_offset,
/* machine driver */
.machine_select = hda_machine_select,
@@ -115,15 +115,6 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.pcm_pointer = hda_dsp_pcm_pointer,
.pcm_ack = hda_dsp_pcm_ack,
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
- /* probe callbacks */
- .probe_assign = hda_probe_compr_assign,
- .probe_free = hda_probe_compr_free,
- .probe_set_params = hda_probe_compr_set_params,
- .probe_trigger = hda_probe_compr_trigger,
- .probe_pointer = hda_probe_compr_pointer,
-#endif
-
/* firmware loading */
.load_firmware = snd_sof_load_firmware_raw,
@@ -146,6 +137,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = {
.trace_release = hda_dsp_trace_release,
.trace_trigger = hda_dsp_trace_trigger,
+ /* client ops */
+ .register_ipc_clients = hda_register_clients,
+ .unregister_ipc_clients = hda_unregister_clients,
+
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/iomem-utils.c
index 66fa6602fb67..3f57f6cf6542 100644
--- a/sound/soc/sof/utils.c
+++ b/sound/soc/sof/iomem-utils.c
@@ -3,7 +3,7 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018-2022 Intel Corporation. All rights reserved.
//
// Author: Keyon Jie <yang.jie@linux.intel.com>
//
@@ -125,62 +125,3 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
return 0;
}
EXPORT_SYMBOL(sof_block_read);
-
-/*
- * Generic buffer page table creation.
- * Take the each physical page address and drop the least significant unused
- * bits from each (based on PAGE_SIZE). Then pack valid page address bits
- * into compressed page table.
- */
-
-int snd_sof_create_page_table(struct device *dev,
- struct snd_dma_buffer *dmab,
- unsigned char *page_table, size_t size)
-{
- int i, pages;
-
- pages = snd_sgbuf_aligned_pages(size);
-
- dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
- dmab->area, size, pages);
-
- for (i = 0; i < pages; i++) {
- /*
- * The number of valid address bits for each page is 20.
- * idx determines the byte position within page_table
- * where the current page's address is stored
- * in the compressed page_table.
- * This can be calculated by multiplying the page number by 2.5.
- */
- u32 idx = (5 * i) >> 1;
- u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
- u8 *pg_table;
-
- dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
-
- pg_table = (u8 *)(page_table + idx);
-
- /*
- * pagetable compression:
- * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
- * ___________pfn 0__________ __________pfn 1___________ _pfn 2...
- * .... .... .... .... .... .... .... .... .... .... ....
- * It is created by:
- * 1. set current location to 0, PFN index i to 0
- * 2. put pfn[i] at current location in Little Endian byte order
- * 3. calculate an intermediate value as
- * x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
- * 4. put x at offset (current location + 2) in LE byte order
- * 5. increment current location by 5 bytes, increment i by 2
- * 6. continue to (2)
- */
- if (i & 1)
- put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
- pg_table);
- else
- put_unaligned_le32(pfn, pg_table);
- }
-
- return pages;
-}
-EXPORT_SYMBOL(snd_sof_create_page_table);
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 5bcf906d90af..5f5753608c79 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -17,26 +17,17 @@
#include "sof-priv.h"
#include "sof-audio.h"
#include "ops.h"
+#include "ipc3-ops.h"
-static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type);
-static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd);
+typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
+
+static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf);
+static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf);
/*
* IPC message Tx/Rx message handling.
*/
-/* SOF generic IPC data */
-struct snd_sof_ipc {
- struct snd_sof_dev *sdev;
-
- /* protects messages and the disable flag */
- struct mutex tx_mutex;
- /* disables further sending of ipc's */
- bool disable_ipc_tx;
-
- struct snd_sof_ipc_msg msg;
-};
-
struct sof_ipc_ctrl_data_params {
size_t msg_bytes;
size_t hdr_bytes;
@@ -294,14 +285,20 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
}
/* send IPC message from host to DSP */
-static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
+static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc,
void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes)
{
+ struct sof_ipc_cmd_hdr *hdr = msg_data;
struct snd_sof_dev *sdev = ipc->sdev;
struct snd_sof_ipc_msg *msg;
int ret;
+ if (!msg_data || msg_bytes < sizeof(*hdr)) {
+ dev_err_ratelimited(sdev->dev, "No IPC message to send\n");
+ return -EINVAL;
+ }
+
if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
return -ENODEV;
@@ -314,15 +311,13 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
/* initialise the message */
msg = &ipc->msg;
- msg->header = header;
+ /* attach message data */
+ msg->msg_data = msg_data;
msg->msg_size = msg_bytes;
+
msg->reply_size = reply_bytes;
msg->reply_error = 0;
- /* attach any data */
- if (msg_bytes)
- memcpy(msg->msg_data, msg_data, msg_bytes);
-
sdev->msg = msg;
ret = snd_sof_dsp_send_msg(sdev, msg);
@@ -339,7 +334,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
return ret;
}
- ipc_log_header(sdev->dev, "ipc tx", msg->header);
+ ipc_log_header(sdev->dev, "ipc tx", hdr->cmd);
/* now wait for completion */
return tx_wait_done(ipc, msg, reply_data);
@@ -385,7 +380,7 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
/* Serialise IPC TX */
mutex_lock(&ipc->tx_mutex);
- ret = sof_ipc_tx_message_unlocked(ipc, header, msg_data, msg_bytes,
+ ret = sof_ipc_tx_message_unlocked(ipc, msg_data, msg_bytes,
reply_data, reply_bytes);
mutex_unlock(&ipc->tx_mutex);
@@ -473,44 +468,32 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
}
EXPORT_SYMBOL(snd_sof_ipc_reply);
-static void ipc_comp_notification(struct snd_sof_dev *sdev,
- struct sof_ipc_cmd_hdr *hdr)
+static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
{
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
- struct sof_ipc_ctrl_data *cdata;
- int ret;
switch (msg_type) {
case SOF_IPC_COMP_GET_VALUE:
case SOF_IPC_COMP_GET_DATA:
- cdata = kmalloc(hdr->size, GFP_KERNEL);
- if (!cdata)
- return;
-
- /* read back full message */
- ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to read component event: %d\n", ret);
- goto err;
- }
break;
default:
dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type);
return;
}
- snd_sof_control_notify(sdev, cdata);
-
-err:
- kfree(cdata);
+ if (tplg_ops->control->update)
+ tplg_ops->control->update(sdev, msg_buf);
}
/* DSP firmware has sent host a message */
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
{
+ ipc_rx_callback rx_callback = NULL;
struct sof_ipc_cmd_hdr hdr;
- u32 cmd, type;
+ void *msg_buf;
+ u32 cmd;
int err;
/* read back header */
@@ -519,10 +502,15 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
return;
}
+
+ if (hdr.size < sizeof(hdr)) {
+ dev_err(sdev->dev, "The received message size is invalid\n");
+ return;
+ }
+
ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
- type = hdr.cmd & SOF_CMD_TYPE_MASK;
/* check message type */
switch (cmd) {
@@ -547,20 +535,38 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
case SOF_IPC_GLB_PM_MSG:
break;
case SOF_IPC_GLB_COMP_MSG:
- ipc_comp_notification(sdev, &hdr);
+ rx_callback = ipc_comp_notification;
break;
case SOF_IPC_GLB_STREAM_MSG:
- /* need to pass msg id into the function */
- ipc_stream_message(sdev, hdr.cmd);
+ rx_callback = ipc_stream_message;
break;
case SOF_IPC_GLB_TRACE_MSG:
- ipc_trace_message(sdev, type);
+ rx_callback = ipc_trace_message;
break;
default:
- dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd);
+ dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
break;
}
+ /* read the full message */
+ msg_buf = kmalloc(hdr.size, GFP_KERNEL);
+ if (!msg_buf)
+ return;
+
+ err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
+ if (err < 0) {
+ dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
+ } else {
+ /* Call local handler for the message */
+ if (rx_callback)
+ rx_callback(sdev, msg_buf);
+
+ /* Notify registered clients */
+ sof_client_ipc_rx_dispatcher(sdev, msg_buf);
+ }
+
+ kfree(msg_buf);
+
ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
}
EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
@@ -569,19 +575,14 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
* IPC trace mechanism.
*/
-static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type)
+static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
{
- struct sof_ipc_dma_trace_posn posn;
- int ret;
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
switch (msg_type) {
case SOF_IPC_TRACE_DMA_POSITION:
- /* read back full message */
- ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
- if (ret < 0)
- dev_warn(sdev->dev, "failed to read trace position: %d\n", ret);
- else
- snd_sof_trace_update_pos(sdev, &posn);
+ snd_sof_trace_update_pos(sdev, msg_buf);
break;
default:
dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type);
@@ -663,11 +664,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
}
/* stream notifications from DSP FW */
-static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd)
+static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
{
- /* get msg cmd type and msd id */
- u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK;
- u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd);
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
+ u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
switch (msg_type) {
case SOF_IPC_STREAM_POSITION:
@@ -789,7 +790,6 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
memcpy(sparams->dst, sparams->src + offset, send_bytes);
err = sof_ipc_tx_message_unlocked(sdev->ipc,
- partdata->rhdr.hdr.cmd,
partdata,
partdata->rhdr.hdr.size,
partdata,
@@ -815,7 +815,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set)
{
struct snd_soc_component *scomp = scontrol->scomp;
- struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
@@ -1000,9 +1000,6 @@ int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev)
struct snd_sof_ipc_msg *msg;
msg = &sdev->ipc->msg;
- msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- if (!msg->msg_data)
- return -ENOMEM;
msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
if (!msg->reply_data)
@@ -1029,6 +1026,19 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
init_waitqueue_head(&msg->waitq);
+ /*
+ * Use IPC3 ops as it is the only available version now. With the addition of new IPC
+ * versions, this will need to be modified to use the selected version at runtime.
+ */
+ ipc->ops = &ipc3_ops;
+
+ /* check for mandatory ops */
+ if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget ||
+ !ipc->ops->tplg->control) {
+ dev_err(sdev->dev, "Invalid IPC ops\n");
+ return NULL;
+ }
+
return ipc;
}
EXPORT_SYMBOL(snd_sof_ipc_init);
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
new file mode 100644
index 000000000000..cdd5ad860a94
--- /dev/null
+++ b/sound/soc/sof/ipc3-control.c
@@ -0,0 +1,594 @@
+// 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) 2021 Intel Corporation. All rights reserved.
+//
+//
+
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ipc3-ops.h"
+
+static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
+{
+ if (value >= size)
+ return volume_map[size - 1];
+
+ return volume_map[value];
+}
+
+static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (volume_map[i] >= value)
+ return i;
+ }
+
+ return i - 1;
+}
+
+static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ int ret;
+
+ if (!scontrol->comp_data_dirty)
+ return;
+
+ if (!pm_runtime_active(scomp->dev))
+ return;
+
+ /* set the ABI header values */
+ cdata->data->magic = SOF_ABI_MAGIC;
+ cdata->data->abi = SOF_ABI_VERSION;
+
+ /* refresh the component data from DSP */
+ scontrol->comp_data_dirty = false;
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
+
+ /* Set the flag to re-try next time to get the data */
+ scontrol->comp_data_dirty = true;
+ }
+}
+
+static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+
+ snd_sof_refresh_control(scontrol);
+
+ /* read back each channel */
+ for (i = 0; i < channels; i++)
+ ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,
+ scontrol->volume_table,
+ scontrol->max + 1);
+
+ return 0;
+}
+
+static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+ bool change = false;
+
+ /* update each channel */
+ for (i = 0; i < channels; i++) {
+ u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],
+ scontrol->volume_table, scontrol->max + 1);
+
+ change = change || (value != cdata->chanv[i].value);
+ cdata->chanv[i].channel = i;
+ cdata->chanv[i].value = value;
+ }
+
+ /* notify DSP of mixer updates */
+ if (pm_runtime_active(scomp->dev)) {
+ int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
+ scontrol->name);
+ return false;
+ }
+ }
+
+ return change;
+}
+
+static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+
+ snd_sof_refresh_control(scontrol);
+
+ /* read back each channel */
+ for (i = 0; i < channels; i++)
+ ucontrol->value.integer.value[i] = cdata->chanv[i].value;
+
+ return 0;
+}
+
+static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+ bool change = false;
+ u32 value;
+
+ /* update each channel */
+ for (i = 0; i < channels; i++) {
+ value = ucontrol->value.integer.value[i];
+ change = change || (value != cdata->chanv[i].value);
+ cdata->chanv[i].channel = i;
+ cdata->chanv[i].value = value;
+ }
+
+ /* notify DSP of mixer updates */
+ if (pm_runtime_active(scomp->dev)) {
+ int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
+ scontrol->name);
+ return false;
+ }
+ }
+
+ return change;
+}
+
+static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+
+ snd_sof_refresh_control(scontrol);
+
+ /* read back each channel */
+ for (i = 0; i < channels; i++)
+ ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
+
+ return 0;
+}
+
+static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ unsigned int channels = scontrol->num_channels;
+ unsigned int i;
+ bool change = false;
+ u32 value;
+
+ /* update each channel */
+ for (i = 0; i < channels; i++) {
+ value = ucontrol->value.enumerated.item[i];
+ change = change || (value != cdata->chanv[i].value);
+ cdata->chanv[i].channel = i;
+ cdata->chanv[i].value = value;
+ }
+
+ /* notify DSP of enum updates */
+ if (pm_runtime_active(scomp->dev)) {
+ int ret = snd_sof_ipc_set_get_comp_data(scontrol, true);
+
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to set enum updates for %s\n",
+ scontrol->name);
+ return false;
+ }
+ }
+
+ return change;
+}
+
+static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_abi_hdr *data = cdata->data;
+ size_t size;
+
+ snd_sof_refresh_control(scontrol);
+
+ if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+ dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
+ scontrol->max_size);
+ return -EINVAL;
+ }
+
+ /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
+ if (data->size > scontrol->max_size - sizeof(*data)) {
+ dev_err_ratelimited(scomp->dev,
+ "%u bytes of control data is invalid, max is %zu\n",
+ data->size, scontrol->max_size - sizeof(*data));
+ return -EINVAL;
+ }
+
+ size = data->size + sizeof(*data);
+
+ /* copy back to kcontrol */
+ memcpy(ucontrol->value.bytes.data, data, size);
+
+ return 0;
+}
+
+static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_abi_hdr *data = cdata->data;
+ size_t size;
+
+ if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+ dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
+ scontrol->max_size);
+ return -EINVAL;
+ }
+
+ /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
+ if (data->size > scontrol->max_size - sizeof(*data)) {
+ dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n",
+ data->size, scontrol->max_size - sizeof(*data));
+ return -EINVAL;
+ }
+
+ size = data->size + sizeof(*data);
+
+ /* copy from kcontrol */
+ memcpy(data, ucontrol->value.bytes.data, size);
+
+ /* notify DSP of byte control updates */
+ if (pm_runtime_active(scomp->dev))
+ return snd_sof_ipc_set_get_comp_data(scontrol, true);
+
+ return 0;
+}
+
+static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data, unsigned int size)
+{
+ struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct snd_ctl_tlv header;
+ size_t data_size;
+
+ snd_sof_refresh_control(scontrol);
+
+ /*
+ * Decrement the limit by ext bytes header size to
+ * ensure the user space buffer is not exceeded.
+ */
+ if (size < sizeof(struct snd_ctl_tlv))
+ return -ENOSPC;
+
+ size -= sizeof(struct snd_ctl_tlv);
+
+ /* set the ABI header values */
+ cdata->data->magic = SOF_ABI_MAGIC;
+ cdata->data->abi = SOF_ABI_VERSION;
+
+ /* check data size doesn't exceed max coming from topology */
+ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
+ cdata->data->size,
+ scontrol->max_size - sizeof(struct sof_abi_hdr));
+ return -EINVAL;
+ }
+
+ data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
+
+ /* make sure we don't exceed size provided by user space for data */
+ if (data_size > size)
+ return -ENOSPC;
+
+ header.numid = cdata->cmd;
+ header.length = data_size;
+ if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
+ return -EFAULT;
+
+ if (copy_to_user(tlvd->tlv, cdata->data, data_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data;
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct snd_ctl_tlv header;
+
+ /*
+ * The beginning of bytes data contains a header from where
+ * the length (as bytes) is needed to know the correct copy
+ * length of data from tlvd->tlv.
+ */
+ if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))
+ return -EFAULT;
+
+ /* make sure TLV info is consistent */
+ if (header.length + sizeof(struct snd_ctl_tlv) > size) {
+ dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n",
+ header.length, sizeof(struct snd_ctl_tlv), size);
+ return -EINVAL;
+ }
+
+ /* be->max is coming from topology */
+ if (header.length > scontrol->max_size) {
+ dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n",
+ header.length, scontrol->max_size);
+ return -EINVAL;
+ }
+
+ /* Check that header id matches the command */
+ if (header.numid != cdata->cmd) {
+ dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n",
+ header.numid);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(cdata->data, tlvd->tlv, header.length))
+ return -EFAULT;
+
+ if (cdata->data->magic != SOF_ABI_MAGIC) {
+ dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic);
+ return -EINVAL;
+ }
+
+ if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
+ dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n",
+ cdata->data->abi);
+ return -EINVAL;
+ }
+
+ /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
+ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
+ return -EINVAL;
+ }
+
+ /* notify DSP of byte control updates */
+ if (pm_runtime_active(scomp->dev))
+ return snd_sof_ipc_set_get_comp_data(scontrol, true);
+
+ return 0;
+}
+
+static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+ struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct snd_ctl_tlv header;
+ size_t data_size;
+ int ret;
+
+ /*
+ * Decrement the limit by ext bytes header size to
+ * ensure the user space buffer is not exceeded.
+ */
+ if (size < sizeof(struct snd_ctl_tlv))
+ return -ENOSPC;
+
+ size -= sizeof(struct snd_ctl_tlv);
+
+ /* set the ABI header values */
+ cdata->data->magic = SOF_ABI_MAGIC;
+ cdata->data->abi = SOF_ABI_VERSION;
+
+ /* get all the component data from DSP */
+ ret = snd_sof_ipc_set_get_comp_data(scontrol, false);
+ if (ret < 0)
+ return ret;
+
+ /* check data size doesn't exceed max coming from topology */
+ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
+ cdata->data->size,
+ scontrol->max_size - sizeof(struct sof_abi_hdr));
+ return -EINVAL;
+ }
+
+ data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
+
+ /* make sure we don't exceed size provided by user space for data */
+ if (data_size > size)
+ return -ENOSPC;
+
+ header.numid = cdata->cmd;
+ header.length = data_size;
+ if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
+ return -EFAULT;
+
+ if (copy_to_user(tlvd->tlv, cdata->data, data_size))
+ return -EFAULT;
+
+ return ret;
+}
+
+static void snd_sof_update_control(struct snd_sof_control *scontrol,
+ struct sof_ipc_ctrl_data *cdata)
+{
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_ipc_ctrl_data *local_cdata;
+ int i;
+
+ local_cdata = scontrol->ipc_control_data;
+
+ if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
+ if (cdata->num_elems != local_cdata->data->size) {
+ dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n",
+ cdata->num_elems, local_cdata->data->size);
+ return;
+ }
+
+ /* copy the new binary data */
+ memcpy(local_cdata->data, cdata->data, cdata->num_elems);
+ } else if (cdata->num_elems != scontrol->num_channels) {
+ dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n",
+ cdata->num_elems, scontrol->num_channels);
+ } else {
+ /* copy the new values */
+ for (i = 0; i < cdata->num_elems; i++)
+ local_cdata->chanv[i].value = cdata->chanv[i].value;
+ }
+}
+
+static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message)
+{
+ struct sof_ipc_ctrl_data *cdata = ipc_control_message;
+ struct snd_soc_dapm_widget *widget;
+ struct snd_sof_control *scontrol;
+ struct snd_sof_widget *swidget;
+ struct snd_kcontrol *kc = NULL;
+ struct soc_mixer_control *sm;
+ struct soc_bytes_ext *be;
+ size_t expected_size;
+ struct soc_enum *se;
+ bool found = false;
+ int i, type;
+
+ if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET ||
+ cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) {
+ dev_err(sdev->dev, "Component data is not supported in control notification\n");
+ return;
+ }
+
+ /* Find the swidget first */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->comp_id == cdata->comp_id) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ /* Translate SOF cmd to TPLG type */
+ switch (cdata->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_SWITCH:
+ type = SND_SOC_TPLG_TYPE_MIXER;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ type = SND_SOC_TPLG_TYPE_BYTES;
+ break;
+ case SOF_CTRL_CMD_ENUM:
+ type = SND_SOC_TPLG_TYPE_ENUM;
+ break;
+ default:
+ dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__);
+ return;
+ }
+
+ widget = swidget->widget;
+ for (i = 0; i < widget->num_kcontrols; i++) {
+ /* skip non matching types or non matching indexes within type */
+ if (widget->dobj.widget.kcontrol_type[i] == type &&
+ widget->kcontrol_news[i].index == cdata->index) {
+ kc = widget->kcontrols[i];
+ break;
+ }
+ }
+
+ if (!kc)
+ return;
+
+ switch (cdata->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_SWITCH:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ scontrol = sm->dobj.private;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ be = (struct soc_bytes_ext *)kc->private_value;
+ scontrol = be->dobj.private;
+ break;
+ case SOF_CTRL_CMD_ENUM:
+ se = (struct soc_enum *)kc->private_value;
+ scontrol = se->dobj.private;
+ break;
+ default:
+ return;
+ }
+
+ expected_size = sizeof(struct sof_ipc_ctrl_data);
+ switch (cdata->type) {
+ case SOF_CTRL_TYPE_VALUE_CHAN_GET:
+ case SOF_CTRL_TYPE_VALUE_CHAN_SET:
+ expected_size += cdata->num_elems *
+ sizeof(struct sof_ipc_ctrl_value_chan);
+ break;
+ case SOF_CTRL_TYPE_DATA_GET:
+ case SOF_CTRL_TYPE_DATA_SET:
+ expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
+ break;
+ default:
+ return;
+ }
+
+ if (cdata->rhdr.hdr.size != expected_size) {
+ dev_err(sdev->dev, "Component notification size mismatch\n");
+ return;
+ }
+
+ if (cdata->num_elems)
+ /*
+ * The message includes the updated value/data, update the
+ * control's local cache using the received notification
+ */
+ snd_sof_update_control(scontrol, cdata);
+ else
+ /* Mark the scontrol that the value/data is changed in SOF */
+ scontrol->comp_data_dirty = true;
+
+ snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
+}
+
+const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
+ .volume_put = sof_ipc3_volume_put,
+ .volume_get = sof_ipc3_volume_get,
+ .switch_put = sof_ipc3_switch_put,
+ .switch_get = sof_ipc3_switch_get,
+ .enum_put = sof_ipc3_enum_put,
+ .enum_get = sof_ipc3_enum_get,
+ .bytes_put = sof_ipc3_bytes_put,
+ .bytes_get = sof_ipc3_bytes_get,
+ .bytes_ext_put = sof_ipc3_bytes_ext_put,
+ .bytes_ext_get = sof_ipc3_bytes_ext_get,
+ .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,
+ .update = sof_ipc3_control_update,
+};
diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h
new file mode 100644
index 000000000000..a4784626a3d7
--- /dev/null
+++ b/sound/soc/sof/ipc3-ops.h
@@ -0,0 +1,21 @@
+/* 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) 2021 Intel Corporation. All rights reserved.
+ *
+ * Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_SOF_IPC3_OPS_H
+#define __SOUND_SOC_SOF_IPC3_OPS_H
+
+#include "sof-priv.h"
+
+extern const struct sof_ipc_tplg_ops ipc3_tplg_ops;
+extern const struct sof_ipc_ops ipc3_ops;
+extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
+extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
+
+#endif
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
new file mode 100644
index 000000000000..58b75943cf6d
--- /dev/null
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -0,0 +1,372 @@
+// 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) 2021 Intel Corporation. All rights reserved.
+//
+//
+
+#include <sound/pcm_params.h>
+#include "ipc3-ops.h"
+#include "ops.h"
+#include "sof-priv.h"
+#include "sof-audio.h"
+
+static int sof_ipc3_pcm_hw_free(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_ipc_stream stream;
+ struct sof_ipc_reply reply;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ if (!spcm->prepared[substream->stream])
+ return 0;
+
+ stream.hdr.size = sizeof(stream);
+ stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
+ stream.comp_id = spcm->stream[substream->stream].comp_id;
+
+ /* send IPC to the DSP */
+ return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
+ sizeof(stream), &reply, sizeof(reply));
+}
+
+static int sof_ipc3_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_ipc_fw_version *v = &sdev->fw_ready.version;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sof_ipc_pcm_params_reply ipc_params_reply;
+ struct sof_ipc_pcm_params pcm;
+ struct snd_sof_pcm *spcm;
+ int ret;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ memset(&pcm, 0, sizeof(pcm));
+
+ /* number of pages should be rounded up */
+ pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes);
+
+ /* set IPC PCM parameters */
+ pcm.hdr.size = sizeof(pcm);
+ pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
+ pcm.comp_id = spcm->stream[substream->stream].comp_id;
+ pcm.params.hdr.size = sizeof(pcm.params);
+ pcm.params.buffer.phy_addr = spcm->stream[substream->stream].page_table.addr;
+ pcm.params.buffer.size = runtime->dma_bytes;
+ pcm.params.direction = substream->stream;
+ pcm.params.sample_valid_bytes = params_width(params) >> 3;
+ pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
+ pcm.params.rate = params_rate(params);
+ pcm.params.channels = params_channels(params);
+ pcm.params.host_period_bytes = params_period_bytes(params);
+
+ /* container size */
+ ret = snd_pcm_format_physical_width(params_format(params));
+ if (ret < 0)
+ return ret;
+ pcm.params.sample_container_bytes = ret >> 3;
+
+ /* format */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update the IPC message with information from the platform */
+ pcm.params.stream_tag = platform_params->stream_tag;
+
+ if (platform_params->use_phy_address)
+ pcm.params.buffer.phy_addr = platform_params->phy_addr;
+
+ if (platform_params->no_ipc_position) {
+ /* For older ABIs set host_period_bytes to zero to inform
+ * FW we don't want position updates. Newer versions use
+ * no_stream_position for this purpose.
+ */
+ if (v->abi_version < SOF_ABI_VER(3, 10, 0))
+ pcm.params.host_period_bytes = 0;
+ else
+ pcm.params.no_stream_position = 1;
+ }
+
+ dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
+
+ /* send hw_params IPC to the DSP */
+ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ &ipc_params_reply, sizeof(ipc_params_reply));
+ if (ret < 0) {
+ dev_err(component->dev, "HW params ipc failed for stream %d\n",
+ pcm.params.stream_tag);
+ return ret;
+ }
+
+ ret = snd_sof_set_stream_data_offset(sdev, substream, 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);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct sof_ipc_stream stream;
+ struct sof_ipc_reply reply;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return -EINVAL;
+
+ stream.hdr.size = sizeof(stream);
+ stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
+ stream.comp_id = spcm->stream[substream->stream].comp_id;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
+ stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
+ break;
+ default:
+ dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
+ return -EINVAL;
+ }
+
+ /* send IPC to the DSP */
+ return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
+ sizeof(stream), &reply, sizeof(reply));
+}
+
+static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
+ struct snd_pcm_hw_params *params)
+{
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai *dai;
+ int i;
+
+ /*
+ * Search for all matching DAIs as we can have both playback and capture DAI
+ * associated with the same link.
+ */
+ list_for_each_entry(dai, &sdev->dai_list, list) {
+ if (!dai->name || strcmp(link_name, dai->name))
+ continue;
+ for (i = 0; i < dai->number_configs; i++) {
+ struct sof_dai_private_data *private = dai->private;
+
+ config = &private->dai_config[i];
+ if (config->ssp.fsync_rate == params_rate(params)) {
+ dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i);
+ dai->current_config = i;
+ break;
+ }
+ }
+ }
+}
+
+static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name);
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ 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_dai_private_data *private;
+ struct snd_soc_dpcm *dpcm;
+
+ if (!dai) {
+ dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
+ private = dai->private;
+ if (!private) {
+ dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__,
+ rtd->dai_link->name);
+ return -EINVAL;
+ }
+
+ /* read format from topology */
+ snd_mask_none(fmt);
+
+ switch (private->comp_dai->config.frame_fmt) {
+ case SOF_IPC_FRAME_S16_LE:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ break;
+ case SOF_IPC_FRAME_S24_4LE:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ break;
+ case SOF_IPC_FRAME_S32_LE:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+ break;
+ default:
+ dev_err(component->dev, "No available DAI format!\n");
+ return -EINVAL;
+ }
+
+ /* read rate and channels from topology */
+ switch (private->dai_config->type) {
+ case SOF_DAI_INTEL_SSP:
+ /* search for config to pcm params match, if not found use default */
+ ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
+
+ rate->min = private->dai_config[dai->current_config].ssp.fsync_rate;
+ rate->max = private->dai_config[dai->current_config].ssp.fsync_rate;
+ channels->min = private->dai_config[dai->current_config].ssp.tdm_slots;
+ channels->max = private->dai_config[dai->current_config].ssp.tdm_slots;
+
+ dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+
+ break;
+ case SOF_DAI_INTEL_DMIC:
+ /* DMIC only supports 16 or 32 bit formats */
+ if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) {
+ dev_err(component->dev, "Invalid fmt %d for DAI type %d\n",
+ private->comp_dai->config.frame_fmt,
+ private->dai_config->type);
+ }
+ break;
+ case SOF_DAI_INTEL_HDA:
+ /*
+ * HDAudio does not follow the default trigger
+ * sequence due to firmware implementation
+ */
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
+ struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+ fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
+ SND_SOC_DPCM_TRIGGER_POST;
+ }
+ break;
+ case SOF_DAI_INTEL_ALH:
+ /*
+ * Dai could run with different channel count compared with
+ * front end, so get dai channel count from topology
+ */
+ channels->min = private->dai_config->alh.channels;
+ channels->max = private->dai_config->alh.channels;
+ break;
+ case SOF_DAI_IMX_ESAI:
+ rate->min = private->dai_config->esai.fsync_rate;
+ rate->max = private->dai_config->esai.fsync_rate;
+ channels->min = private->dai_config->esai.tdm_slots;
+ channels->max = private->dai_config->esai.tdm_slots;
+
+ dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_MEDIATEK_AFE:
+ rate->min = private->dai_config->afe.rate;
+ rate->max = private->dai_config->afe.rate;
+ channels->min = private->dai_config->afe.channels;
+ channels->max = private->dai_config->afe.channels;
+
+ dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_IMX_SAI:
+ rate->min = private->dai_config->sai.fsync_rate;
+ rate->max = private->dai_config->sai.fsync_rate;
+ channels->min = private->dai_config->sai.tdm_slots;
+ channels->max = private->dai_config->sai.tdm_slots;
+
+ dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_AMD_BT:
+ rate->min = private->dai_config->acpbt.fsync_rate;
+ rate->max = private->dai_config->acpbt.fsync_rate;
+ channels->min = private->dai_config->acpbt.tdm_slots;
+ channels->max = private->dai_config->acpbt.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_AMD_SP:
+ rate->min = private->dai_config->acpsp.fsync_rate;
+ rate->max = private->dai_config->acpsp.fsync_rate;
+ channels->min = private->dai_config->acpsp.tdm_slots;
+ channels->max = private->dai_config->acpsp.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ case SOF_DAI_AMD_DMIC:
+ rate->min = private->dai_config->acpdmic.fsync_rate;
+ rate->max = private->dai_config->acpdmic.fsync_rate;
+ channels->min = private->dai_config->acpdmic.tdm_slots;
+ channels->max = private->dai_config->acpdmic.tdm_slots;
+
+ dev_dbg(component->dev,
+ "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
+ break;
+ }
+
+ return 0;
+}
+
+const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
+ .hw_params = sof_ipc3_pcm_hw_params,
+ .hw_free = sof_ipc3_pcm_hw_free,
+ .trigger = sof_ipc3_pcm_trigger,
+ .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
+};
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
new file mode 100644
index 000000000000..2f8450a8c0a1
--- /dev/null
+++ b/sound/soc/sof/ipc3-topology.c
@@ -0,0 +1,2393 @@
+// 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) 2021 Intel Corporation. All rights reserved.
+//
+//
+
+#include <uapi/sound/sof/tokens.h>
+#include <sound/pcm_params.h>
+#include "sof-priv.h"
+#include "sof-audio.h"
+#include "ipc3-ops.h"
+#include "ops.h"
+
+/* Full volume for default values */
+#define VOL_ZERO_DB BIT(VOLUME_FWL)
+
+struct sof_widget_data {
+ int ctrl_type;
+ int ipc_cmd;
+ struct sof_abi_hdr *pdata;
+ struct snd_sof_control *control;
+};
+
+struct sof_process_types {
+ const char *name;
+ enum sof_ipc_process_type type;
+ enum sof_comp_type comp_type;
+};
+
+static const struct sof_process_types sof_process[] = {
+ {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR},
+ {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR},
+ {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT},
+ {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB},
+ {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR},
+ {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX},
+ {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX},
+ {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK},
+ {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP},
+};
+
+static enum sof_ipc_process_type find_process(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sof_process); i++) {
+ if (strcmp(name, sof_process[i].name) == 0)
+ return sof_process[i].type;
+ }
+
+ return SOF_PROCESS_NONE;
+}
+
+static int get_token_process_type(void *elem, void *object, u32 offset)
+{
+ u32 *val = (u32 *)((u8 *)object + offset);
+
+ *val = find_process((const char *)elem);
+ return 0;
+}
+
+/* Buffers */
+static const struct sof_topology_token buffer_tokens[] = {
+ {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_buffer, size)},
+ {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_buffer, caps)},
+};
+
+/* DAI */
+static const struct sof_topology_token dai_tokens[] = {
+ {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
+ offsetof(struct sof_ipc_comp_dai, type)},
+ {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_dai, dai_index)},
+ {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_dai, direction)},
+};
+
+/* BE DAI link */
+static const struct sof_topology_token dai_link_tokens[] = {
+ {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
+ offsetof(struct sof_ipc_dai_config, type)},
+ {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_config, dai_index)},
+};
+
+/* scheduling */
+static const struct sof_topology_token sched_tokens[] = {
+ {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, period)},
+ {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, priority)},
+ {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, period_mips)},
+ {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, core)},
+ {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, frames_per_sched)},
+ {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_pipe_new, time_domain)},
+};
+
+static const struct sof_topology_token pipeline_tokens[] = {
+ {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_widget, dynamic_pipeline_widget)},
+
+};
+
+/* volume */
+static const struct sof_topology_token volume_tokens[] = {
+ {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_volume, ramp)},
+ {SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_volume, initial_ramp)},
+};
+
+/* SRC */
+static const struct sof_topology_token src_tokens[] = {
+ {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_src, source_rate)},
+ {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_src, sink_rate)},
+};
+
+/* ASRC */
+static const struct sof_topology_token asrc_tokens[] = {
+ {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_asrc, source_rate)},
+ {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_asrc, sink_rate)},
+ {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)},
+ {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_asrc, operation_mode)},
+};
+
+/* EFFECT */
+static const struct sof_topology_token process_tokens[] = {
+ {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type,
+ offsetof(struct sof_ipc_comp_process, type)},
+};
+
+/* PCM */
+static const struct sof_topology_token pcm_tokens[] = {
+ {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_host, dmac_config)},
+};
+
+/* Generic components */
+static const struct sof_topology_token comp_tokens[] = {
+ {SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_config, periods_sink)},
+ {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp_config, periods_source)},
+ {SOF_TKN_COMP_FORMAT,
+ SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
+ offsetof(struct sof_ipc_comp_config, frame_fmt)},
+};
+
+/* SSP */
+static const struct sof_topology_token ssp_tokens[] = {
+ {SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_ssp_params, clks_control)},
+ {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_ssp_params, mclk_id)},
+ {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)},
+ {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)},
+ {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_ssp_params, quirks)},
+ {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct sof_ipc_dai_ssp_params, tdm_per_slot_padding_flag)},
+ {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)},
+};
+
+/* ALH */
+static const struct sof_topology_token alh_tokens[] = {
+ {SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_alh_params, rate)},
+ {SOF_TKN_INTEL_ALH_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_alh_params, channels)},
+};
+
+/* DMIC */
+static const struct sof_topology_token dmic_tokens[] = {
+ {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)},
+ {SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)},
+ {SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)},
+ {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)},
+ {SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_params, duty_min)},
+ {SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_params, duty_max)},
+ {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, num_pdm_active)},
+ {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)},
+ {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)},
+};
+
+/* ESAI */
+static const struct sof_topology_token esai_tokens[] = {
+ {SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_esai_params, mclk_id)},
+};
+
+/* SAI */
+static const struct sof_topology_token sai_tokens[] = {
+ {SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_sai_params, mclk_id)},
+};
+
+/*
+ * DMIC PDM Tokens
+ * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token
+ * as it increments the index while parsing the array of pdm tokens
+ * and determines the correct offset
+ */
+static const struct sof_topology_token dmic_pdm_tokens[] = {
+ {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id)},
+ {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)},
+ {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)},
+ {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)},
+ {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)},
+ {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)},
+ {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)},
+};
+
+/* HDA */
+static const struct sof_topology_token hda_tokens[] = {
+ {SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_hda_params, rate)},
+ {SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_hda_params, channels)},
+};
+
+/* AFE */
+static const struct sof_topology_token afe_tokens[] = {
+ {SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, rate)},
+ {SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, channels)},
+ {SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
+ offsetof(struct sof_ipc_dai_mtk_afe_params, format)},
+};
+
+/* Core tokens */
+static const struct sof_topology_token core_tokens[] = {
+ {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_comp, core)},
+};
+
+/* Component extended tokens */
+static const struct sof_topology_token comp_ext_tokens[] = {
+ {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
+ offsetof(struct snd_sof_widget, uuid)},
+};
+
+static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {
+ [SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)},
+ [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},
+ [SOF_SCHED_TOKENS] = {"Scheduler tokens", sched_tokens, ARRAY_SIZE(sched_tokens)},
+ [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)},
+ [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)},
+ [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)},
+ [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)},
+ [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)},
+ [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
+ [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)},
+ [SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)},
+ [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},
+ [SOF_DAI_LINK_TOKENS] = {"DAI link tokens", dai_link_tokens, ARRAY_SIZE(dai_link_tokens)},
+ [SOF_HDA_TOKENS] = {"HDA tokens", hda_tokens, ARRAY_SIZE(hda_tokens)},
+ [SOF_SSP_TOKENS] = {"SSP tokens", ssp_tokens, ARRAY_SIZE(ssp_tokens)},
+ [SOF_ALH_TOKENS] = {"ALH tokens", alh_tokens, ARRAY_SIZE(alh_tokens)},
+ [SOF_DMIC_TOKENS] = {"DMIC tokens", dmic_tokens, ARRAY_SIZE(dmic_tokens)},
+ [SOF_DMIC_PDM_TOKENS] = {"DMIC PDM tokens", dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)},
+ [SOF_ESAI_TOKENS] = {"ESAI tokens", esai_tokens, ARRAY_SIZE(esai_tokens)},
+ [SOF_SAI_TOKENS] = {"SAI tokens", sai_tokens, ARRAY_SIZE(sai_tokens)},
+ [SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)},
+};
+
+/**
+ * sof_comp_alloc - allocate and initialize buffer for a new component
+ * @swidget: pointer to struct snd_sof_widget containing extended data
+ * @ipc_size: IPC payload size that will be updated depending on valid
+ * extended data.
+ * @index: ID of the pipeline the component belongs to
+ *
+ * Return: The pointer to the new allocated component, NULL if failed.
+ */
+static void *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size,
+ int index)
+{
+ struct sof_ipc_comp *comp;
+ size_t total_size = *ipc_size;
+ size_t ext_size = sizeof(swidget->uuid);
+
+ /* only non-zero UUID is valid */
+ if (!guid_is_null(&swidget->uuid))
+ total_size += ext_size;
+
+ comp = kzalloc(total_size, GFP_KERNEL);
+ if (!comp)
+ return NULL;
+
+ /* configure comp new IPC message */
+ comp->hdr.size = total_size;
+ comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW;
+ comp->id = swidget->comp_id;
+ comp->pipeline_id = index;
+ comp->core = swidget->core;
+
+ /* handle the extended data if needed */
+ if (total_size > *ipc_size) {
+ /* append extended data to the end of the component */
+ memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size);
+ comp->ext_data_length = ext_size;
+ }
+
+ /* update ipc_size and return */
+ *ipc_size = total_size;
+ return comp;
+}
+
+static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config)
+{
+ dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n",
+ config->periods_sink, config->periods_source,
+ config->frame_fmt);
+}
+
+static int sof_ipc3_widget_setup_comp_host(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_host *host;
+ size_t ipc_size = sizeof(*host);
+ int ret;
+
+ host = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!host)
+ return -ENOMEM;
+ swidget->private = host;
+
+ /* configure host comp IPC message */
+ host->comp.type = SOF_COMP_HOST;
+ host->config.hdr.size = sizeof(host->config);
+
+ if (swidget->id == snd_soc_dapm_aif_out)
+ host->direction = SOF_IPC_STREAM_CAPTURE;
+ else
+ host->direction = SOF_IPC_STREAM_PLAYBACK;
+
+ /* parse one set of pcm_tokens */
+ ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*host), 1);
+ if (ret < 0)
+ goto err;
+
+ /* parse one set of comp_tokens */
+ ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(host->config), 1);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name);
+ sof_dbg_comp_config(scomp, &host->config);
+
+ return 0;
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+}
+
+static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget)
+{
+ kfree(swidget->private);
+}
+
+static int sof_ipc3_widget_setup_comp_tone(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_tone *tone;
+ size_t ipc_size = sizeof(*tone);
+ int ret;
+
+ tone = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!tone)
+ return -ENOMEM;
+
+ swidget->private = tone;
+
+ /* configure siggen IPC message */
+ tone->comp.type = SOF_COMP_TONE;
+ tone->config.hdr.size = sizeof(tone->config);
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &tone->config, SOF_COMP_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(tone->config), 1);
+ if (ret < 0) {
+ kfree(swidget->private);
+ swidget->private = NULL;
+ return ret;
+ }
+
+ dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n",
+ swidget->widget->name, tone->frequency, tone->amplitude);
+ sof_dbg_comp_config(scomp, &tone->config);
+
+ return 0;
+}
+
+static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_mixer *mixer;
+ size_t ipc_size = sizeof(*mixer);
+ int ret;
+
+ mixer = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!mixer)
+ return -ENOMEM;
+
+ swidget->private = mixer;
+
+ /* configure mixer IPC message */
+ mixer->comp.type = SOF_COMP_MIXER;
+ mixer->config.hdr.size = sizeof(mixer->config);
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &mixer->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples,
+ sizeof(mixer->config), 1);
+ if (ret < 0) {
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+ }
+
+ dev_dbg(scomp->dev, "loaded mixer %s\n", swidget->widget->name);
+ sof_dbg_comp_config(scomp, &mixer->config);
+
+ return 0;
+}
+
+static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_pipe_new *pipeline;
+ struct snd_sof_widget *comp_swidget;
+ int ret;
+
+ pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);
+ if (!pipeline)
+ return -ENOMEM;
+
+ /* configure pipeline IPC message */
+ pipeline->hdr.size = sizeof(*pipeline);
+ pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW;
+ pipeline->pipeline_id = swidget->pipeline_id;
+ pipeline->comp_id = swidget->comp_id;
+
+ swidget->private = pipeline;
+
+ /* component at start of pipeline is our stream id */
+ comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname);
+ if (!comp_swidget) {
+ dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n",
+ swidget->widget->name, swidget->widget->sname);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pipeline->sched_id = comp_swidget->comp_id;
+
+ /* parse one set of scheduler tokens */
+ ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*pipeline), 1);
+ if (ret < 0)
+ goto err;
+
+ /* parse one set of pipeline tokens */
+ ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*swidget), 1);
+ if (ret < 0)
+ goto err;
+
+ if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
+ pipeline->core = SOF_DSP_PRIMARY_CORE;
+
+ if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))
+ swidget->dynamic_pipeline_widget =
+ sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE);
+
+ dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n",
+ swidget->widget->name, pipeline->period, pipeline->priority,
+ pipeline->period_mips, pipeline->core, pipeline->frames_per_sched,
+ swidget->dynamic_pipeline_widget);
+
+ swidget->core = pipeline->core;
+
+ return 0;
+
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+}
+
+static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_buffer *buffer;
+ int ret;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ swidget->private = buffer;
+
+ /* configure dai IPC message */
+ buffer->comp.hdr.size = sizeof(*buffer);
+ buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW;
+ buffer->comp.id = swidget->comp_id;
+ buffer->comp.type = SOF_COMP_BUFFER;
+ buffer->comp.pipeline_id = swidget->pipeline_id;
+ buffer->comp.core = swidget->core;
+
+ /* parse one set of buffer tokens */
+ ret = sof_update_ipc_object(scomp, buffer, SOF_BUFFER_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*buffer), 1);
+ if (ret < 0) {
+ kfree(swidget->private);
+ swidget->private = NULL;
+ return ret;
+ }
+
+ dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n",
+ swidget->widget->name, buffer->size, buffer->caps);
+
+ return 0;
+}
+
+static int sof_ipc3_widget_setup_comp_src(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_src *src;
+ size_t ipc_size = sizeof(*src);
+ int ret;
+
+ src = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!src)
+ return -ENOMEM;
+
+ swidget->private = src;
+
+ /* configure src IPC message */
+ src->comp.type = SOF_COMP_SRC;
+ src->config.hdr.size = sizeof(src->config);
+
+ /* parse one set of src tokens */
+ ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*src), 1);
+ if (ret < 0)
+ goto err;
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples, sizeof(src->config), 1);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n",
+ swidget->widget->name, src->source_rate, src->sink_rate);
+ sof_dbg_comp_config(scomp, &src->config);
+
+ return 0;
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+}
+
+static int sof_ipc3_widget_setup_comp_asrc(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_asrc *asrc;
+ size_t ipc_size = sizeof(*asrc);
+ int ret;
+
+ asrc = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!asrc)
+ return -ENOMEM;
+
+ swidget->private = asrc;
+
+ /* configure ASRC IPC message */
+ asrc->comp.type = SOF_COMP_ASRC;
+ asrc->config.hdr.size = sizeof(asrc->config);
+
+ /* parse one set of asrc tokens */
+ ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*asrc), 1);
+ if (ret < 0)
+ goto err;
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d asynch %d operation %d\n",
+ swidget->widget->name, asrc->source_rate, asrc->sink_rate,
+ asrc->asynchronous_mode, asrc->operation_mode);
+
+ sof_dbg_comp_config(scomp, &asrc->config);
+
+ return 0;
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+}
+
+/*
+ * Mux topology
+ */
+static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_mux *mux;
+ size_t ipc_size = sizeof(*mux);
+ int ret;
+
+ mux = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!mux)
+ return -ENOMEM;
+
+ swidget->private = mux;
+
+ /* configure mux IPC message */
+ mux->comp.type = SOF_COMP_MUX;
+ mux->config.hdr.size = sizeof(mux->config);
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &mux->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples, sizeof(mux->config), 1);
+ if (ret < 0) {
+ kfree(swidget->private);
+ swidget->private = NULL;
+ return ret;
+ }
+
+ dev_dbg(scomp->dev, "loaded mux %s\n", swidget->widget->name);
+ sof_dbg_comp_config(scomp, &mux->config);
+
+ return 0;
+}
+
+/*
+ * PGA Topology
+ */
+
+static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc_comp_volume *volume;
+ struct snd_sof_control *scontrol;
+ size_t ipc_size = sizeof(*volume);
+ int min_step, max_step;
+ int ret;
+
+ volume = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!volume)
+ return -ENOMEM;
+
+ swidget->private = volume;
+
+ /* configure volume IPC message */
+ volume->comp.type = SOF_COMP_VOLUME;
+ volume->config.hdr.size = sizeof(volume->config);
+
+ /* parse one set of volume tokens */
+ ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*volume), 1);
+ if (ret < 0)
+ goto err;
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples,
+ sizeof(volume->config), 1);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name);
+ sof_dbg_comp_config(scomp, &volume->config);
+
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+ if (scontrol->comp_id == swidget->comp_id &&
+ scontrol->volume_table) {
+ min_step = scontrol->min_volume_step;
+ max_step = scontrol->max_volume_step;
+ volume->min_value = scontrol->volume_table[min_step];
+ volume->max_value = scontrol->volume_table[max_step];
+ volume->channels = scontrol->num_channels;
+ break;
+ }
+ }
+
+ return 0;
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+
+ return ret;
+}
+
+static int sof_get_control_data(struct snd_soc_component *scomp,
+ struct snd_soc_dapm_widget *widget,
+ struct sof_widget_data *wdata, size_t *size)
+{
+ const struct snd_kcontrol_new *kc;
+ struct sof_ipc_ctrl_data *cdata;
+ struct soc_mixer_control *sm;
+ struct soc_bytes_ext *sbe;
+ struct soc_enum *se;
+ int i;
+
+ *size = 0;
+
+ for (i = 0; i < widget->num_kcontrols; i++) {
+ kc = &widget->kcontrol_news[i];
+
+ switch (widget->dobj.widget.kcontrol_type[i]) {
+ case SND_SOC_TPLG_TYPE_MIXER:
+ sm = (struct soc_mixer_control *)kc->private_value;
+ wdata[i].control = sm->dobj.private;
+ break;
+ case SND_SOC_TPLG_TYPE_BYTES:
+ sbe = (struct soc_bytes_ext *)kc->private_value;
+ wdata[i].control = sbe->dobj.private;
+ break;
+ case SND_SOC_TPLG_TYPE_ENUM:
+ se = (struct soc_enum *)kc->private_value;
+ wdata[i].control = se->dobj.private;
+ break;
+ default:
+ dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n",
+ widget->dobj.widget.kcontrol_type[i], widget->name);
+ return -EINVAL;
+ }
+
+ if (!wdata[i].control) {
+ dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name);
+ return -EINVAL;
+ }
+
+ cdata = wdata[i].control->ipc_control_data;
+ wdata[i].pdata = cdata->data;
+ if (!wdata[i].pdata)
+ return -EINVAL;
+
+ /* make sure data is valid - data can be updated at runtime */
+ if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES &&
+ wdata[i].pdata->magic != SOF_ABI_MAGIC)
+ return -EINVAL;
+
+ *size += wdata[i].pdata->size;
+
+ /* get data type */
+ switch (cdata->cmd) {
+ case SOF_CTRL_CMD_VOLUME:
+ case SOF_CTRL_CMD_ENUM:
+ case SOF_CTRL_CMD_SWITCH:
+ wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE;
+ wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
+ break;
+ case SOF_CTRL_CMD_BINARY:
+ wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA;
+ wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int sof_process_load(struct snd_soc_component *scomp,
+ struct snd_sof_widget *swidget, int type)
+{
+ struct snd_soc_dapm_widget *widget = swidget->widget;
+ struct sof_ipc_comp_process *process;
+ struct sof_widget_data *wdata = NULL;
+ size_t ipc_data_size = 0;
+ size_t ipc_size;
+ int offset = 0;
+ int ret;
+ int i;
+
+ /* allocate struct for widget control data sizes and types */
+ if (widget->num_kcontrols) {
+ wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL);
+ if (!wdata)
+ return -ENOMEM;
+
+ /* get possible component controls and get size of all pdata */
+ ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size);
+ if (ret < 0)
+ goto out;
+ }
+
+ ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size;
+
+ /* we are exceeding max ipc size, config needs to be sent separately */
+ if (ipc_size > SOF_IPC_MSG_MAX_SIZE) {
+ ipc_size -= ipc_data_size;
+ ipc_data_size = 0;
+ }
+
+ process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!process) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ swidget->private = process;
+
+ /* configure iir IPC message */
+ process->comp.type = type;
+ process->config.hdr.size = sizeof(process->config);
+
+ /* parse one set of comp tokens */
+ ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples,
+ sizeof(process->config), 1);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name);
+ sof_dbg_comp_config(scomp, &process->config);
+
+ /*
+ * found private data in control, so copy it.
+ * get possible component controls - get size of all pdata,
+ * then memcpy with headers
+ */
+ if (ipc_data_size) {
+ for (i = 0; i < widget->num_kcontrols; i++) {
+ memcpy(&process->data[offset],
+ wdata[i].pdata->data,
+ wdata[i].pdata->size);
+ offset += wdata[i].pdata->size;
+ }
+ }
+
+ process->size = ipc_data_size;
+
+ kfree(wdata);
+
+ return 0;
+err:
+ kfree(swidget->private);
+ swidget->private = NULL;
+out:
+ kfree(wdata);
+ return ret;
+}
+
+static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sof_process); i++) {
+ if (sof_process[i].type == type)
+ return sof_process[i].comp_type;
+ }
+
+ return SOF_COMP_NONE;
+}
+
+/*
+ * Processing Component Topology - can be "effect", "codec", or general
+ * "processing".
+ */
+
+static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc_comp_process config;
+ int ret;
+
+ memset(&config, 0, sizeof(config));
+ config.comp.core = swidget->core;
+
+ /* parse one set of process tokens */
+ ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(config), 1);
+ if (ret < 0)
+ return ret;
+
+ /* now load process specific data and send IPC */
+ return sof_process_load(scomp, swidget, find_process_comp_type(config.type));
+}
+
+static int sof_link_hda_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* init IPC */
+ memset(&config->hda, 0, sizeof(config->hda));
+ config->hdr.size = size;
+
+ /* parse one set of HDA tokens */
+ ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples,
+ slink->num_tuples, size, 1);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(scomp->dev, "HDA config rate %d channels %d\n",
+ config->hda.rate, config->hda.channels);
+
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config,
+ struct sof_ipc_dai_config *config)
+{
+ /* clock directions wrt codec */
+ config->format &= ~SOF_DAI_FMT_CLOCK_PROVIDER_MASK;
+ if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) {
+ /* codec is bclk provider */
+ if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)
+ config->format |= SOF_DAI_FMT_CBP_CFP;
+ else
+ config->format |= SOF_DAI_FMT_CBP_CFC;
+ } else {
+ /* codec is bclk consumer */
+ if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)
+ config->format |= SOF_DAI_FMT_CBC_CFP;
+ else
+ config->format |= SOF_DAI_FMT_CBC_CFC;
+ }
+
+ /* inverted clocks ? */
+ config->format &= ~SOF_DAI_FMT_INV_MASK;
+ if (hw_config->invert_bclk) {
+ if (hw_config->invert_fsync)
+ config->format |= SOF_DAI_FMT_IB_IF;
+ else
+ config->format |= SOF_DAI_FMT_IB_NF;
+ } else {
+ if (hw_config->invert_fsync)
+ config->format |= SOF_DAI_FMT_NB_IF;
+ else
+ config->format |= SOF_DAI_FMT_NB_NF;
+ }
+}
+
+static int sof_link_sai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->sai, 0, sizeof(config->sai));
+ config->hdr.size = size;
+
+ /* parse one set of SAI tokens */
+ ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples,
+ slink->num_tuples, size, 1);
+ if (ret < 0)
+ return ret;
+
+ config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
+ config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
+ config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->sai.mclk_direction = hw_config->mclk_direction;
+
+ config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
+ config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots);
+ config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots);
+
+ dev_info(scomp->dev,
+ "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
+ config->dai_index, config->format,
+ config->sai.mclk_rate, config->sai.tdm_slot_width,
+ config->sai.tdm_slots, config->sai.mclk_id);
+
+ if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) {
+ dev_err(scomp->dev, "Invalid channel count for SAI%d\n", config->dai_index);
+ return -EINVAL;
+ }
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->esai, 0, sizeof(config->esai));
+ config->hdr.size = size;
+
+ /* parse one set of ESAI tokens */
+ ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples,
+ slink->num_tuples, size, 1);
+ if (ret < 0)
+ return ret;
+
+ config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
+ config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
+ config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->esai.mclk_direction = hw_config->mclk_direction;
+ config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
+ config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);
+ config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);
+
+ dev_info(scomp->dev,
+ "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
+ config->dai_index, config->format,
+ config->esai.mclk_rate, config->esai.tdm_slot_width,
+ config->esai.tdm_slots, config->esai.mclk_id);
+
+ if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {
+ dev_err(scomp->dev, "Invalid channel count for ESAI%d\n", config->dai_index);
+ return -EINVAL;
+ }
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpdmic, 0, sizeof(config->acpdmic));
+ config->hdr.size = size;
+
+ config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpdmic.tdm_slots,
+ config->acpdmic.fsync_rate);
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpbt, 0, sizeof(config->acpbt));
+ config->hdr.size = size;
+
+ config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpbt.tdm_slots,
+ config->acpbt.fsync_rate);
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_acp_sp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->acpsp, 0, sizeof(config->acpsp));
+ config->hdr.size = size;
+
+ config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+
+ dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n",
+ config->dai_index, config->acpsp.tdm_slots,
+ config->acpsp.fsync_rate);
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ config->hdr.size = size;
+
+ /* parse the required set of AFE tokens based on num_hw_cfgs */
+ ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n",
+ config->afe.rate, config->afe.channels, config->afe.format);
+
+ config->afe.stream_id = DMA_CHAN_INVALID;
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int current_config = 0;
+ int i, ret;
+
+ /*
+ * Parse common data, we should have 1 common data per hw_config.
+ */
+ ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ /* process all possible hw configs */
+ for (i = 0; i < slink->num_hw_configs; i++) {
+ if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id)
+ current_config = i;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(&hw_config[i], &config[i]);
+
+ config[i].hdr.size = size;
+
+ /* copy differentiating hw configs to ipc structs */
+ config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate);
+ config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate);
+ config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate);
+ config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots);
+ config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width);
+ config[i].ssp.mclk_direction = hw_config[i].mclk_direction;
+ config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots);
+ config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots);
+
+ dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n",
+ config[i].dai_index, config[i].format,
+ config[i].ssp.mclk_rate, config[i].ssp.bclk_rate,
+ config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits,
+ config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots,
+ config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control);
+
+ /* validate SSP fsync rate and channel count */
+ if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) {
+ dev_err(scomp->dev, "Invalid fsync rate for SSP%d\n", config[i].dai_index);
+ return -EINVAL;
+ }
+
+ if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) {
+ dev_err(scomp->dev, "Invalid channel count for SSP%d\n",
+ config[i].dai_index);
+ return -EINVAL;
+ }
+ }
+
+ dai->number_configs = slink->num_hw_configs;
+ dai->current_config = current_config;
+ private->dai_config = kmemdup(config, size * slink->num_hw_configs, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_dai_private_data *private = dai->private;
+ struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
+ struct sof_ipc_fw_version *v = &ready->version;
+ size_t size = sizeof(*config);
+ int i, ret;
+
+ /* Ensure the entire DMIC config struct is zeros */
+ memset(&config->dmic, 0, sizeof(config->dmic));
+
+ /* parse the required set of DMIC tokens based on num_hw_cfgs */
+ ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ /* parse the required set of DMIC PDM tokens based on number of active PDM's */
+ ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS,
+ slink->tuples, slink->num_tuples,
+ sizeof(struct sof_ipc_dai_dmic_pdm_ctrl),
+ config->dmic.num_pdm_active);
+ if (ret < 0)
+ return ret;
+
+ /* set IPC header size */
+ config->hdr.size = size;
+
+ /* debug messages */
+ dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n",
+ config->dai_index, config->dmic.driver_ipc_version);
+ dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %d\n",
+ config->dmic.pdmclk_min, config->dmic.pdmclk_max,
+ config->dmic.duty_min);
+ dev_dbg(scomp->dev, "duty_max %d fifo_fs %d num_pdms active %d\n",
+ config->dmic.duty_max, config->dmic.fifo_fs,
+ config->dmic.num_pdm_active);
+ dev_dbg(scomp->dev, "fifo word length %d\n", config->dmic.fifo_bits);
+
+ for (i = 0; i < config->dmic.num_pdm_active; i++) {
+ dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n",
+ config->dmic.pdm[i].id,
+ config->dmic.pdm[i].enable_mic_a,
+ config->dmic.pdm[i].enable_mic_b);
+ dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n",
+ config->dmic.pdm[i].id,
+ config->dmic.pdm[i].polarity_mic_a,
+ config->dmic.pdm[i].polarity_mic_b);
+ dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n",
+ config->dmic.pdm[i].id,
+ config->dmic.pdm[i].clk_edge,
+ config->dmic.pdm[i].skew);
+ }
+
+ /*
+ * this takes care of backwards compatible handling of fifo_bits_b.
+ * It is deprecated since firmware ABI version 3.0.1.
+ */
+ if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1))
+ config->dmic.fifo_bits_b = config->dmic.fifo_bits;
+
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* parse the required set of ALH tokens based on num_hw_cfgs */
+ ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ /* init IPC */
+ config->hdr.size = size;
+
+ /* set config for all DAI's with name matching the link name */
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_dai_private_data *private;
+ struct sof_ipc_comp_dai *comp_dai;
+ size_t ipc_size = sizeof(*comp_dai);
+ struct sof_ipc_dai_config *config;
+ struct snd_sof_dai_link *slink;
+ int ret;
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ dai->private = private;
+
+ private->comp_dai = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);
+ if (!private->comp_dai) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ /* configure dai IPC message */
+ comp_dai = private->comp_dai;
+ comp_dai->comp.type = SOF_COMP_DAI;
+ comp_dai->config.hdr.size = sizeof(comp_dai->config);
+
+ /* parse one set of DAI tokens */
+ ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*comp_dai), 1);
+ if (ret < 0)
+ goto free;
+
+ /* update comp_tokens */
+ ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS,
+ swidget->tuples, swidget->num_tuples,
+ sizeof(comp_dai->config), 1);
+ if (ret < 0)
+ goto free;
+
+ dev_dbg(scomp->dev, "%s dai %s: type %d index %d\n",
+ __func__, swidget->widget->name, comp_dai->type, comp_dai->dai_index);
+ sof_dbg_comp_config(scomp, &comp_dai->config);
+
+ /* now update DAI config */
+ list_for_each_entry(slink, &sdev->dai_link_list, list) {
+ struct sof_ipc_dai_config common_config;
+ int i;
+
+ if (strcmp(slink->link->name, dai->name))
+ continue;
+
+ /* Reserve memory for all hw configs, eventually freed by widget */
+ config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL);
+ if (!config) {
+ ret = -ENOMEM;
+ goto free_comp;
+ }
+
+ /* parse one set of DAI link tokens */
+ ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS,
+ slink->tuples, slink->num_tuples,
+ sizeof(common_config), 1);
+ if (ret < 0)
+ goto free_config;
+
+ for (i = 0; i < slink->num_hw_configs; i++) {
+ config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
+ config[i].format = le32_to_cpu(slink->hw_configs[i].fmt);
+ config[i].type = common_config.type;
+ config[i].dai_index = comp_dai->dai_index;
+ }
+
+ switch (common_config.type) {
+ case SOF_DAI_INTEL_SSP:
+ ret = sof_link_ssp_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_INTEL_DMIC:
+ ret = sof_link_dmic_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_INTEL_HDA:
+ ret = sof_link_hda_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_INTEL_ALH:
+ ret = sof_link_alh_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_IMX_SAI:
+ ret = sof_link_sai_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_IMX_ESAI:
+ ret = sof_link_esai_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_AMD_BT:
+ ret = sof_link_acp_bt_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_AMD_SP:
+ ret = sof_link_acp_sp_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_AMD_DMIC:
+ ret = sof_link_acp_dmic_load(scomp, slink, config, dai);
+ break;
+ case SOF_DAI_MEDIATEK_AFE:
+ ret = sof_link_afe_load(scomp, slink, config, dai);
+ break;
+ default:
+ break;
+ }
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name);
+ goto free_config;
+ }
+
+ kfree(config);
+ }
+
+ return 0;
+free_config:
+ kfree(config);
+free_comp:
+ kfree(comp_dai);
+free:
+ kfree(private);
+ dai->private = NULL;
+ return ret;
+}
+
+static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget)
+{
+ switch (swidget->id) {
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_dai_out:
+ {
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_dai_private_data *dai_data;
+
+ if (!dai)
+ return;
+
+ dai_data = dai->private;
+ if (dai_data) {
+ kfree(dai_data->comp_dai);
+ kfree(dai_data->dai_config);
+ kfree(dai_data);
+ }
+ kfree(dai);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
+{
+ struct sof_ipc_pipe_comp_connect connect;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ connect.hdr.size = sizeof(connect);
+ connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT;
+ connect.source_id = sroute->src_widget->comp_id;
+ connect.sink_id = sroute->sink_widget->comp_id;
+
+ dev_dbg(sdev->dev, "setting up route %s -> %s\n",
+ sroute->src_widget->widget->name,
+ sroute->sink_widget->widget->name);
+
+ /* send ipc */
+ ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect),
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,
+ sroute->src_widget->widget->name, sroute->sink_widget->widget->name);
+
+ return ret;
+}
+
+static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_ctrl_data *cdata;
+ int ret;
+
+ scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
+ if (!scontrol->ipc_control_data)
+ return -ENOMEM;
+
+ if (scontrol->max_size < sizeof(*cdata) ||
+ scontrol->max_size < sizeof(struct sof_abi_hdr)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* init the get/put bytes data */
+ if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) {
+ dev_err(sdev->dev, "err: bytes data size %zu exceeds max %zu.\n",
+ scontrol->priv_size, scontrol->max_size - sizeof(*cdata));
+ ret = -EINVAL;
+ goto err;
+ }
+
+ scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size;
+
+ cdata = scontrol->ipc_control_data;
+ cdata->cmd = SOF_CTRL_CMD_BINARY;
+ cdata->index = scontrol->index;
+
+ if (scontrol->priv_size > 0) {
+ memcpy(cdata->data, scontrol->priv, scontrol->priv_size);
+ kfree(scontrol->priv);
+
+ if (cdata->data->magic != SOF_ABI_MAGIC) {
+ dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
+ dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n",
+ cdata->data->abi);
+ ret = -EINVAL;
+ 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");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ kfree(scontrol->ipc_control_data);
+ return ret;
+}
+
+static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_ctrl_data *cdata;
+ int i;
+
+ /* init the volume get/put data */
+ scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);
+
+ scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
+ if (!scontrol->ipc_control_data)
+ return -ENOMEM;
+
+ cdata = scontrol->ipc_control_data;
+ cdata->index = scontrol->index;
+
+ /* set cmd for mixer control */
+ if (scontrol->max == 1) {
+ cdata->cmd = SOF_CTRL_CMD_SWITCH;
+ return 0;
+ }
+
+ cdata->cmd = SOF_CTRL_CMD_VOLUME;
+
+ /* set default volume values to 0dB in control */
+ for (i = 0; i < scontrol->num_channels; i++) {
+ cdata->chanv[i].channel = i;
+ cdata->chanv[i].value = VOL_ZERO_DB;
+ }
+
+ return 0;
+}
+
+static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_ctrl_data *cdata;
+
+ /* init the enum get/put data */
+ scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);
+
+ scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
+ if (!scontrol->ipc_control_data)
+ return -ENOMEM;
+
+ cdata = scontrol->ipc_control_data;
+ cdata->index = scontrol->index;
+ cdata->cmd = SOF_CTRL_CMD_ENUM;
+
+ return 0;
+}
+
+static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ switch (scontrol->info_type) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ return sof_ipc3_control_load_volume(sdev, scontrol);
+ case SND_SOC_TPLG_CTL_BYTES:
+ return sof_ipc3_control_load_bytes(sdev, scontrol);
+ case SND_SOC_TPLG_CTL_ENUM:
+ case SND_SOC_TPLG_CTL_ENUM_VALUE:
+ return sof_ipc3_control_load_enum(sdev, scontrol);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ struct sof_ipc_free fcomp;
+
+ fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;
+ fcomp.hdr.size = sizeof(fcomp);
+ fcomp.id = scontrol->comp_id;
+
+ /* send IPC to the DSP */
+ return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0);
+}
+
+/* send pcm params ipc */
+static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc_pcm_params_reply ipc_params_reply;
+ struct snd_pcm_hw_params *params;
+ struct sof_ipc_pcm_params pcm;
+ struct snd_sof_pcm *spcm;
+ int ret;
+
+ /* get runtime PCM params using widget's stream name */
+ spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
+ if (!spcm) {
+ dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name);
+ return -EINVAL;
+ }
+
+ params = &spcm->params[dir];
+
+ /* set IPC PCM params */
+ memset(&pcm, 0, sizeof(pcm));
+ pcm.hdr.size = sizeof(pcm);
+ pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
+ pcm.comp_id = swidget->comp_id;
+ pcm.params.hdr.size = sizeof(pcm.params);
+ pcm.params.direction = dir;
+ pcm.params.sample_valid_bytes = params_width(params) >> 3;
+ pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
+ pcm.params.rate = params_rate(params);
+ pcm.params.channels = params_channels(params);
+ pcm.params.host_period_bytes = params_period_bytes(params);
+
+ /* set format */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32:
+ pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* send IPC to the DSP */
+ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
+ &ipc_params_reply, sizeof(ipc_params_reply));
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
+ swidget->widget->name);
+
+ return ret;
+}
+
+ /* send stream trigger ipc */
+static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc_stream stream;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ /* set IPC stream params */
+ stream.hdr.size = sizeof(stream);
+ stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd;
+ stream.comp_id = swidget->comp_id;
+
+ /* send IPC to the DSP */
+ ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
+ sizeof(stream), &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
+
+ return ret;
+}
+
+static int sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_soc_component *scomp;
+ int stream = SNDRV_PCM_STREAM_CAPTURE;
+ struct snd_sof_pcm *spcm;
+ int ret = 0;
+
+ if (!swidget)
+ return 0;
+
+ scomp = swidget->scomp;
+
+ dev_dbg(scomp->dev, "received event %d for widget %s\n",
+ event, w->name);
+
+ /* get runtime PCM params using widget's stream name */
+ spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
+ if (!spcm) {
+ dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__,
+ swidget->widget->name);
+ return -EINVAL;
+ }
+
+ /* process events */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (spcm->stream[stream].suspend_ignored) {
+ dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
+ return 0;
+ }
+
+ /* set pcm params */
+ ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream);
+ if (ret < 0) {
+ dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n",
+ __func__, swidget->widget->name);
+ break;
+ }
+
+ /* start trigger */
+ ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START);
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
+ swidget->widget->name);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (spcm->stream[stream].suspend_ignored) {
+ dev_dbg(scomp->dev,
+ "POST_PMD event ignored, KWD pipeline will remain RUNNING\n");
+ return 0;
+ }
+
+ /* stop trigger */
+ ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,
+ swidget->widget->name);
+
+ /* pcm free */
+ ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE);
+ if (ret < 0)
+ dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__,
+ swidget->widget->name);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* event handlers for keyword detect component */
+static const struct snd_soc_tplg_widget_events sof_kwd_events[] = {
+ {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_ipc3_keyword_dapm_event},
+};
+
+static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp,
+ struct snd_sof_widget *swidget, u16 event_type)
+{
+ struct sof_ipc_comp *ipc_comp;
+
+ /* validate widget event type */
+ switch (event_type) {
+ case SOF_KEYWORD_DETECT_DAPM_EVENT:
+ /* only KEYWORD_DETECT comps should handle this */
+ if (swidget->id != snd_soc_dapm_effect)
+ break;
+
+ ipc_comp = swidget->private;
+ if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT)
+ break;
+
+ /* bind event to keyword detect comp */
+ return snd_soc_tplg_widget_bind_event(swidget->widget, sof_kwd_events,
+ ARRAY_SIZE(sof_kwd_events), event_type);
+ default:
+ break;
+ }
+
+ dev_err(scomp->dev, "Invalid event type %d for widget %s\n", event_type,
+ swidget->widget->name);
+
+ return -EINVAL;
+}
+
+static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_pipe_ready ready;
+ struct sof_ipc_reply reply;
+ int ret;
+
+ dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
+ swidget->widget->name, swidget->comp_id);
+
+ memset(&ready, 0, sizeof(ready));
+ ready.hdr.size = sizeof(ready);
+ ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
+ ready.comp_id = swidget->comp_id;
+
+ ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply,
+ sizeof(reply));
+ if (ret < 0)
+ return ret;
+
+ return 1;
+}
+
+static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_free ipc_free = {
+ .hdr = {
+ .size = sizeof(ipc_free),
+ .cmd = SOF_IPC_GLB_TPLG_MSG,
+ },
+ .id = swidget->comp_id,
+ };
+ struct sof_ipc_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ {
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
+ break;
+ }
+ case snd_soc_dapm_buffer:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
+ break;
+ default:
+ ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
+ break;
+ }
+
+ ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);
+
+ return ret;
+}
+
+static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ unsigned int flags, struct snd_sof_dai_config_data *data)
+{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_dai_private_data *private;
+ struct sof_ipc_dai_config *config;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ if (!dai || !dai->private) {
+ dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name);
+ return -EINVAL;
+ }
+
+ private = dai->private;
+ if (!private->dai_config) {
+ dev_err(sdev->dev, "No config for DAI %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ config = &private->dai_config[dai->current_config];
+ if (!config) {
+ dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ switch (config->type) {
+ case SOF_DAI_INTEL_SSP:
+ /*
+ * DAI_CONFIG IPC during hw_params/hw_free for SSP DAI's is not supported in older
+ * firmware
+ */
+ if (v->abi_version < SOF_ABI_VER(3, 18, 0) &&
+ ((flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) ||
+ (flags & SOF_DAI_CONFIG_FLAGS_HW_FREE)))
+ return 0;
+ break;
+ case SOF_DAI_INTEL_HDA:
+ if (data)
+ config->hda.link_dma_ch = data->dai_data;
+ break;
+ case SOF_DAI_INTEL_ALH:
+ if (data) {
+ config->dai_index = data->dai_index;
+ config->alh.stream_id = data->dai_data;
+ }
+ break;
+ default:
+ break;
+ }
+
+ config->flags = flags;
+
+ /* only send the IPC if the widget is set up in the DSP */
+ if (swidget->use_count > 0) {
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
+ &reply, sizeof(reply));
+ if (ret < 0)
+ dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);
+ }
+
+ return ret;
+}
+
+static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ struct sof_ipc_comp_reply reply;
+ int ret;
+
+ if (!swidget->private)
+ return 0;
+
+ switch (swidget->id) {
+ case snd_soc_dapm_dai_in:
+ case snd_soc_dapm_dai_out:
+ {
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_dai_private_data *dai_data = dai->private;
+ struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;
+
+ ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai,
+ comp->hdr.size, &reply, sizeof(reply));
+ break;
+ }
+ case snd_soc_dapm_scheduler:
+ {
+ struct sof_ipc_pipe_new *pipeline;
+
+ pipeline = swidget->private;
+ ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
+ sizeof(*pipeline), &reply, sizeof(reply));
+ break;
+ }
+ default:
+ {
+ struct sof_ipc_cmd_hdr *hdr;
+
+ hdr = swidget->private;
+ ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
+ &reply, sizeof(reply));
+ break;
+ }
+ }
+ if (ret < 0)
+ dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name);
+
+ return ret;
+}
+
+static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
+{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
+ struct snd_sof_widget *swidget;
+ struct snd_sof_route *sroute;
+ int ret;
+
+ /* restore pipeline components */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ /* only set up the widgets belonging to static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
+ continue;
+
+ /*
+ * For older firmware, skip scheduler widgets in this loop,
+ * sof_widget_setup() will be called in the 'complete pipeline' loop
+ */
+ if (v->abi_version < SOF_ABI_VER(3, 19, 0) &&
+ swidget->id == snd_soc_dapm_scheduler)
+ continue;
+
+ /* update DAI config. The IPC will be sent in sof_widget_setup() */
+ if (WIDGET_IS_DAI(swidget->id)) {
+ struct snd_sof_dai *dai = swidget->private;
+ struct sof_dai_private_data *private;
+ struct sof_ipc_dai_config *config;
+
+ if (!dai || !dai->private)
+ continue;
+ private = dai->private;
+ if (!private->dai_config)
+ continue;
+
+ config = private->dai_config;
+ /*
+ * The link DMA channel would be invalidated for running
+ * streams but not for streams that were in the PAUSED
+ * state during suspend. So invalidate it here before setting
+ * the dai config in the DSP.
+ */
+ if (config->type == SOF_DAI_INTEL_HDA)
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+ }
+
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* restore pipeline connections */
+ list_for_each_entry(sroute, &sdev->route_list, list) {
+ /* only set up routes belonging to static pipelines */
+ if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||
+ sroute->sink_widget->dynamic_pipeline_widget))
+ continue;
+
+ /*
+ * For virtual routes, both sink and source are not buffer. IPC3 only supports
+ * connections between a buffer and a component. Ignore the rest.
+ */
+ if (sroute->src_widget->id != snd_soc_dapm_buffer &&
+ sroute->sink_widget->id != snd_soc_dapm_buffer)
+ continue;
+
+ ret = sof_route_setup(sdev, sroute->src_widget->widget,
+ sroute->sink_widget->widget);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: route set up failed\n", __func__);
+ return ret;
+ }
+ }
+
+ /* complete pipeline */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ switch (swidget->id) {
+ case snd_soc_dapm_scheduler:
+ /* only complete static pipelines */
+ if (!verify && swidget->dynamic_pipeline_widget)
+ continue;
+
+ if (v->abi_version < SOF_ABI_VER(3, 19, 0)) {
+ ret = sof_widget_setup(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget);
+ if (swidget->complete < 0)
+ return swidget->complete;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that
+ * did not get suspended(ex: paused streams) so the widgets can be set up again during resume.
+ */
+static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_widget *swidget;
+ struct snd_sof_pcm *spcm;
+ int dir, ret;
+
+ /*
+ * free all PCMs and their associated DAPM widgets if their connected DAPM widget
+ * list is not NULL. This should only be true for paused streams at this point.
+ * This is equivalent to the handling of FE DAI suspend trigger for running streams.
+ */
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ struct snd_pcm_substream *substream = spcm->stream[dir].substream;
+
+ if (!substream || !substream->runtime)
+ continue;
+
+ if (spcm->stream[dir].list) {
+ ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ /*
+ * free any left over DAI widgets. This is equivalent to the handling of suspend trigger
+ * for the BE DAI for running streams.
+ */
+ list_for_each_entry(swidget, &sdev->widget_list, list)
+ if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) {
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * For older firmware, this function doesn't free widgets for static pipelines during suspend.
+ * It only resets use_count for all widgets.
+ */
+static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
+{
+ struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
+ struct snd_sof_widget *swidget;
+ struct snd_sof_route *sroute;
+ int ret;
+
+ /*
+ * This function is called during suspend and for one-time topology verification during
+ * first boot. In both cases, there is no need to protect swidget->use_count and
+ * sroute->setup because during suspend all running streams are suspended and during
+ * topology loading the sound card unavailable to open PCMs.
+ */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (swidget->dynamic_pipeline_widget)
+ continue;
+
+ /* Do not free widgets for static pipelines with FW ABI older than 3.19 */
+ if (!verify && !swidget->dynamic_pipeline_widget &&
+ v->abi_version < SOF_ABI_VER(3, 19, 0)) {
+ swidget->use_count = 0;
+ swidget->complete = 0;
+ continue;
+ }
+
+ ret = sof_widget_free(sdev, swidget);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Tear down all pipelines associated with PCMs that did not get suspended
+ * and unset the prepare flag so that they can be set up again during resume.
+ * Skip this step for older firmware.
+ */
+ if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) {
+ ret = sof_tear_down_left_over_pipelines(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to tear down paused pipelines\n");
+ return ret;
+ }
+ }
+
+ list_for_each_entry(sroute, &sdev->route_list, list)
+ sroute->setup = false;
+
+ return 0;
+}
+
+static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type)
+{
+ struct sof_dai_private_data *private = dai->private;
+
+ if (!private || !private->dai_config)
+ return 0;
+
+ switch (private->dai_config->type) {
+ case SOF_DAI_INTEL_SSP:
+ switch (clk_type) {
+ case SOF_DAI_CLK_INTEL_SSP_MCLK:
+ return private->dai_config->ssp.mclk_rate;
+ case SOF_DAI_CLK_INTEL_SSP_BCLK:
+ return private->dai_config->ssp.bclk_rate;
+ default:
+ break;
+ }
+ dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type);
+ break;
+ default:
+ /* not yet implemented for platforms other than the above */
+ dev_err(sdev->dev, "DAI type %d not supported yet!\n", private->dai_config->type);
+ break;
+ }
+
+ return -EINVAL;
+}
+
+/* token list for each topology object */
+static enum sof_tokens host_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_PCM_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static enum sof_tokens comp_generic_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static enum sof_tokens buffer_token_list[] = {
+ SOF_BUFFER_TOKENS,
+};
+
+static enum sof_tokens pipeline_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_PIPELINE_TOKENS,
+ SOF_SCHED_TOKENS,
+};
+
+static enum sof_tokens asrc_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_ASRC_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static enum sof_tokens src_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_SRC_TOKENS,
+ SOF_COMP_TOKENS
+};
+
+static enum sof_tokens pga_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_VOLUME_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static enum sof_tokens dai_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_DAI_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static enum sof_tokens process_token_list[] = {
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+ SOF_PROCESS_TOKENS,
+ SOF_COMP_TOKENS,
+};
+
+static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
+ [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp,
+ host_token_list, ARRAY_SIZE(host_token_list), NULL},
+ [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp,
+ host_token_list, ARRAY_SIZE(host_token_list), NULL},
+
+ [snd_soc_dapm_dai_in] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai,
+ dai_token_list, ARRAY_SIZE(dai_token_list), NULL},
+ [snd_soc_dapm_dai_out] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai,
+ dai_token_list, ARRAY_SIZE(dai_token_list), NULL},
+ [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp,
+ buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL},
+ [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp,
+ comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),
+ NULL},
+ [snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp,
+ src_token_list, ARRAY_SIZE(src_token_list), NULL},
+ [snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp,
+ asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL},
+ [snd_soc_dapm_siggen] = {sof_ipc3_widget_setup_comp_tone, sof_ipc3_widget_free_comp,
+ comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),
+ NULL},
+ [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp,
+ pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL},
+ [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp,
+ pga_token_list, ARRAY_SIZE(pga_token_list), NULL},
+ [snd_soc_dapm_mux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp,
+ comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL},
+ [snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp,
+ comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),
+ NULL},
+ [snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp,
+ process_token_list, ARRAY_SIZE(process_token_list),
+ sof_ipc3_widget_bind_event},
+};
+
+const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
+ .widget = tplg_ipc3_widget_ops,
+ .control = &tplg_ipc3_control_ops,
+ .route_setup = sof_ipc3_route_setup,
+ .control_setup = sof_ipc3_control_setup,
+ .control_free = sof_ipc3_control_free,
+ .pipeline_complete = sof_ipc3_complete_pipeline,
+ .token_list = ipc3_token_list,
+ .widget_free = sof_ipc3_widget_free,
+ .widget_setup = sof_ipc3_widget_setup,
+ .dai_config = sof_ipc3_dai_config,
+ .dai_get_clk = sof_ipc3_dai_get_clk,
+ .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
+ .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
+};
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
new file mode 100644
index 000000000000..03e914b62728
--- /dev/null
+++ b/sound/soc/sof/ipc3.c
@@ -0,0 +1,45 @@
+// 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) 2021 Intel Corporation. All rights reserved.
+//
+//
+
+#include "sof-priv.h"
+#include "ipc3-ops.h"
+
+static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
+{
+ struct sof_ipc_pm_ctx pm_ctx = {
+ .hdr.size = sizeof(pm_ctx),
+ .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd,
+ };
+ struct sof_ipc_reply reply;
+
+ /* send ctx save ipc to dsp */
+ return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx,
+ sizeof(pm_ctx), &reply, sizeof(reply));
+}
+
+static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
+{
+ return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
+}
+
+static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
+{
+ return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
+}
+
+static const struct sof_ipc_pm_ops ipc3_pm_ops = {
+ .ctx_save = sof_ipc3_ctx_save,
+ .ctx_restore = sof_ipc3_ctx_restore,
+};
+
+const struct sof_ipc_ops ipc3_ops = {
+ .tplg = &ipc3_tplg_ops,
+ .pm = &ipc3_pm_ops,
+ .pcm = &ipc3_pcm_ops,
+};
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index ffe7456e7713..a19474663767 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -369,10 +369,10 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev,
/* host DMA trace */
static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev,
- u32 *stream_tag)
+ struct sof_ipc_dma_trace_params_ext *dtrace_params)
{
if (sof_ops(sdev)->trace_init)
- return sof_ops(sdev)->trace_init(sdev, stream_tag);
+ return sof_ops(sdev)->trace_init(sdev, dtrace_params);
return 0;
}
@@ -420,11 +420,11 @@ static inline int
snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params)
+ struct snd_sof_platform_stream_params *platform_params)
{
if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params)
- return sof_ops(sdev)->pcm_hw_params(sdev, substream,
- params, ipc_params);
+ return sof_ops(sdev)->pcm_hw_params(sdev, substream, params,
+ platform_params);
return 0;
}
@@ -466,14 +466,17 @@ static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev,
{
return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz);
}
-
-/* host configure DSP HW parameters */
+/* host side configuration of the stream's data offset in stream mailbox area */
static inline int
-snd_sof_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
+snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ size_t posn_offset)
{
- return sof_ops(sdev)->ipc_pcm_params(sdev, substream, reply);
+ if (sof_ops(sdev) && sof_ops(sdev)->set_stream_data_offset)
+ return sof_ops(sdev)->set_stream_data_offset(sdev, substream,
+ posn_offset);
+
+ return 0;
}
/* host stream pointer */
@@ -497,49 +500,6 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
return 0;
}
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-static inline int
-snd_sof_probe_compr_assign(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
-{
- return sof_ops(sdev)->probe_assign(sdev, cstream, dai);
-}
-
-static inline int
-snd_sof_probe_compr_free(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
-{
- return sof_ops(sdev)->probe_free(sdev, cstream, dai);
-}
-
-static inline int
-snd_sof_probe_compr_set_params(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params, struct snd_soc_dai *dai)
-{
- return sof_ops(sdev)->probe_set_params(sdev, cstream, params, dai);
-}
-
-static inline int
-snd_sof_probe_compr_trigger(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai)
-{
- return sof_ops(sdev)->probe_trigger(sdev, cstream, cmd, dai);
-}
-
-static inline int
-snd_sof_probe_compr_pointer(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
-{
- if (sof_ops(sdev) && sof_ops(sdev)->probe_pointer)
- return sof_ops(sdev)->probe_pointer(sdev, cstream, tstamp, dai);
-
- return 0;
-}
-#endif
-
/* 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 37fb8e6cd493..658cd8966c9a 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -15,10 +15,8 @@
#include <sound/sof.h>
#include "sof-priv.h"
#include "sof-audio.h"
+#include "sof-utils.h"
#include "ops.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "sof-probes.h"
-#endif
/* Create DMA buffer page table for DSP */
static int create_page_table(struct snd_soc_component *component,
@@ -38,22 +36,6 @@ static int create_page_table(struct snd_soc_component *component,
spcm->stream[stream].page_table.area, size);
}
-static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
-{
- struct snd_soc_component *scomp = spcm->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
-
- /* validate offset */
- int ret = snd_sof_ipc_pcm_params(sdev, substream, reply);
-
- if (ret < 0)
- dev_err(scomp->dev, "error: got wrong reply for PCM %d\n",
- spcm->pcm.pcm_id);
-
- return ret;
-}
-
/*
* sof pcm period elapse work
*/
@@ -100,32 +82,8 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
-int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
- struct snd_sof_pcm *spcm)
-{
- struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
- int ret;
-
- if (!spcm->prepared[substream->stream])
- return 0;
-
- stream.hdr.size = sizeof(stream);
- stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
- stream.comp_id = spcm->stream[substream->stream].comp_id;
-
- /* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
- if (!ret)
- spcm->prepared[substream->stream] = false;
-
- return ret;
-}
-
-static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev,
- struct snd_soc_pcm_runtime *rtd,
- struct snd_sof_pcm *spcm, int dir)
+int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
+ struct snd_sof_pcm *spcm, int dir)
{
struct snd_soc_dai *dai;
int ret, j;
@@ -161,12 +119,12 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
+ 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_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_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_sof_pcm *spcm;
- struct sof_ipc_pcm_params pcm;
- struct sof_ipc_pcm_params_reply ipc_params_reply;
int ret;
/* nothing to do for BE */
@@ -181,103 +139,51 @@ 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.
*/
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
- if (ret < 0)
- return ret;
+ if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ ret = pcm_ops->hw_free(component, substream);
+ if (ret < 0)
+ return ret;
+
+ spcm->prepared[substream->stream] = false;
+ }
dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
- memset(&pcm, 0, sizeof(pcm));
+ /* if this is a repeated hw_params without hw_free, skip setting up widgets */
+ if (!spcm->stream[substream->stream].list) {
+ ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+ if (ret < 0)
+ return ret;
+ }
/* create compressed page table for audio firmware */
if (runtime->buffer_changed) {
ret = create_page_table(component, substream, runtime->dma_area,
runtime->dma_bytes);
+
if (ret < 0)
return ret;
}
- /* number of pages should be rounded up */
- pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes);
-
- /* set IPC PCM parameters */
- pcm.hdr.size = sizeof(pcm);
- pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
- pcm.comp_id = spcm->stream[substream->stream].comp_id;
- pcm.params.hdr.size = sizeof(pcm.params);
- pcm.params.buffer.phy_addr =
- spcm->stream[substream->stream].page_table.addr;
- pcm.params.buffer.size = runtime->dma_bytes;
- pcm.params.direction = substream->stream;
- pcm.params.sample_valid_bytes = params_width(params) >> 3;
- pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
- pcm.params.rate = params_rate(params);
- pcm.params.channels = params_channels(params);
- pcm.params.host_period_bytes = params_period_bytes(params);
-
- /* container size */
- ret = snd_pcm_format_physical_width(params_format(params));
- if (ret < 0)
- return ret;
- pcm.params.sample_container_bytes = ret >> 3;
-
- /* format */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
- break;
- case SNDRV_PCM_FORMAT_S24:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
- break;
- case SNDRV_PCM_FORMAT_S32:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
- break;
- case SNDRV_PCM_FORMAT_FLOAT:
- pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT;
- break;
- default:
- return -EINVAL;
- }
-
- /* firmware already configured host stream */
- ret = snd_sof_pcm_platform_hw_params(sdev,
- substream,
- params,
- &pcm.params);
+ ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
if (ret < 0) {
- dev_err(component->dev, "error: platform hw params failed\n");
+ dev_err(component->dev, "platform hw params failed\n");
return ret;
}
- dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
-
- /* if this is a repeated hw_params without hw_free, skip setting up widgets */
- if (!spcm->stream[substream->stream].list) {
- ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
+ if (pcm_ops->hw_params) {
+ ret = pcm_ops->hw_params(component, substream, params, &platform_params);
if (ret < 0)
return ret;
}
- /* send hw_params IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
- &ipc_params_reply, sizeof(ipc_params_reply));
- if (ret < 0) {
- dev_err(component->dev, "error: hw params ipc failed for stream %d\n",
- pcm.params.stream_tag);
- return ret;
- }
-
- ret = sof_pcm_dsp_params(spcm, substream, &ipc_params_reply);
- if (ret < 0)
- return ret;
-
spcm->prepared[substream->stream] = true;
/* save pcm hw_params */
memcpy(&spcm->params[substream->stream], params, sizeof(*params));
- return ret;
+ return 0;
}
static int sof_pcm_hw_free(struct snd_soc_component *component,
@@ -285,6 +191,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;
struct snd_sof_pcm *spcm;
int ret, err = 0;
@@ -300,10 +207,13 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
spcm->pcm.pcm_id, substream->stream);
/* free PCM in the DSP */
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
- if (ret < 0)
- err = ret;
+ if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ ret = pcm_ops->hw_free(component, substream);
+ if (ret < 0)
+ err = ret;
+ spcm->prepared[substream->stream] = false;
+ }
/* stop DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
@@ -364,13 +274,12 @@ 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;
struct snd_sof_pcm *spcm;
- struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
bool reset_hw_params = false;
bool free_widget_list = false;
bool ipc_first = false;
- int ret;
+ int ret = 0;
/* nothing to do for BE */
if (rtd->dai_link->no_pcm)
@@ -383,17 +292,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
spcm->pcm.pcm_id, substream->stream, cmd);
- stream.hdr.size = sizeof(stream);
- stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
- stream.comp_id = spcm->stream[substream->stream].comp_id;
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
ipc_first = true;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
break;
case SNDRV_PCM_TRIGGER_START:
if (spcm->stream[substream->stream].suspend_ignored) {
@@ -405,7 +308,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = false;
return 0;
}
- stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
if (sdev->system_suspend_target == SOF_SUSPEND_S0IX &&
@@ -422,13 +324,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
- stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
ipc_first = true;
reset_hw_params = true;
break;
default:
- dev_err(component->dev, "error: unhandled trigger cmd %d\n",
- cmd);
+ dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
return -EINVAL;
}
@@ -439,11 +339,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
if (!ipc_first)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
- /* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
+ if (pcm_ops->trigger)
+ ret = pcm_ops->trigger(component, substream, cmd);
- /* need to STOP DMA even if STOP IPC failed */
+ /* need to STOP DMA even if trigger IPC failed */
if (ipc_first)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
@@ -657,31 +556,6 @@ capture:
return 0;
}
-static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
- struct snd_pcm_hw_params *params)
-{
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *dai;
- int i;
-
- /*
- * Search for all matching DAIs as we can have both playback and capture DAI
- * associated with the same link.
- */
- list_for_each_entry(dai, &sdev->dai_list, list) {
- if (!dai->name || strcmp(link_name, dai->name))
- continue;
- for (i = 0; i < dai->number_configs; i++) {
- config = &dai->dai_config[i];
- if (config->ssp.fsync_rate == params_rate(params)) {
- dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i);
- dai->current_config = i;
- break;
- }
- }
- }
-}
-
/* fixup the BE DAI link to match any values from topology */
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params)
{
@@ -695,7 +569,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);
- struct snd_soc_dpcm *dpcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
/* no topology exists for this BE, try a common configuration */
if (!dai) {
@@ -716,148 +590,8 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
return 0;
}
- /* read format from topology */
- snd_mask_none(fmt);
-
- switch (dai->comp_dai.config.frame_fmt) {
- case SOF_IPC_FRAME_S16_LE:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
- break;
- case SOF_IPC_FRAME_S24_4LE:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
- break;
- case SOF_IPC_FRAME_S32_LE:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
- break;
- default:
- dev_err(component->dev, "error: No available DAI format!\n");
- return -EINVAL;
- }
-
- /* read rate and channels from topology */
- switch (dai->dai_config->type) {
- case SOF_DAI_INTEL_SSP:
- /* search for config to pcm params match, if not found use default */
- ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
-
- rate->min = dai->dai_config[dai->current_config].ssp.fsync_rate;
- rate->max = dai->dai_config[dai->current_config].ssp.fsync_rate;
- channels->min = dai->dai_config[dai->current_config].ssp.tdm_slots;
- channels->max = dai->dai_config[dai->current_config].ssp.tdm_slots;
-
- dev_dbg(component->dev,
- "rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
-
- break;
- case SOF_DAI_INTEL_DMIC:
- /* DMIC only supports 16 or 32 bit formats */
- if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) {
- dev_err(component->dev,
- "error: invalid fmt %d for DAI type %d\n",
- dai->comp_dai.config.frame_fmt,
- dai->dai_config->type);
- }
- break;
- case SOF_DAI_INTEL_HDA:
- /*
- * HDAudio does not follow the default trigger
- * sequence due to firmware implementation
- */
- for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
- struct snd_soc_pcm_runtime *fe = dpcm->fe;
-
- fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
- SND_SOC_DPCM_TRIGGER_POST;
- }
- break;
- case SOF_DAI_INTEL_ALH:
- /*
- * Dai could run with different channel count compared with
- * front end, so get dai channel count from topology
- */
- channels->min = dai->dai_config->alh.channels;
- channels->max = dai->dai_config->alh.channels;
- break;
- case SOF_DAI_IMX_ESAI:
- rate->min = dai->dai_config->esai.fsync_rate;
- rate->max = dai->dai_config->esai.fsync_rate;
- channels->min = dai->dai_config->esai.tdm_slots;
- channels->max = dai->dai_config->esai.tdm_slots;
-
- dev_dbg(component->dev,
- "rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- case SOF_DAI_MEDIATEK_AFE:
- rate->min = dai->dai_config->afe.rate;
- rate->max = dai->dai_config->afe.rate;
- channels->min = dai->dai_config->afe.channels;
- channels->max = dai->dai_config->afe.channels;
-
- dev_dbg(component->dev,
- "rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- case SOF_DAI_IMX_SAI:
- rate->min = dai->dai_config->sai.fsync_rate;
- rate->max = dai->dai_config->sai.fsync_rate;
- channels->min = dai->dai_config->sai.tdm_slots;
- channels->max = dai->dai_config->sai.tdm_slots;
-
- dev_dbg(component->dev,
- "rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- case SOF_DAI_AMD_BT:
- rate->min = dai->dai_config->acpbt.fsync_rate;
- rate->max = dai->dai_config->acpbt.fsync_rate;
- channels->min = dai->dai_config->acpbt.tdm_slots;
- channels->max = dai->dai_config->acpbt.tdm_slots;
-
- dev_dbg(component->dev,
- "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "AMD_BT channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- case SOF_DAI_AMD_SP:
- rate->min = dai->dai_config->acpsp.fsync_rate;
- rate->max = dai->dai_config->acpsp.fsync_rate;
- channels->min = dai->dai_config->acpsp.tdm_slots;
- channels->max = dai->dai_config->acpsp.tdm_slots;
-
- dev_dbg(component->dev,
- "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "AMD_SP channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- case SOF_DAI_AMD_DMIC:
- rate->min = dai->dai_config->acpdmic.fsync_rate;
- rate->max = dai->dai_config->acpdmic.fsync_rate;
- channels->min = dai->dai_config->acpdmic.tdm_slots;
- channels->max = dai->dai_config->acpdmic.tdm_slots;
-
- dev_dbg(component->dev,
- "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
- dev_dbg(component->dev,
- "AMD_DMIC channels_min: %d channels_max: %d\n",
- channels->min, channels->max);
- break;
- default:
- dev_err(component->dev, "error: invalid DAI type %d\n",
- dai->dai_config->type);
- break;
- }
+ if (pcm_ops->dai_link_fixup)
+ return pcm_ops->dai_link_fixup(rtd, params);
return 0;
}
@@ -924,9 +658,10 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->pointer = sof_pcm_pointer;
pd->ack = sof_pcm_ack;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- pd->compress_ops = &sof_probe_compressed_ops;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
+ pd->compress_ops = &sof_compressed_ops;
#endif
+
pd->pcm_construct = sof_pcm_new;
pd->ignore_machine = drv_name;
pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 197a88695fef..1c319582ca6f 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -48,22 +48,6 @@ static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev)
return target_dsp_state;
}
-static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
-{
- struct sof_ipc_pm_ctx pm_ctx;
- struct sof_ipc_reply reply;
-
- memset(&pm_ctx, 0, sizeof(pm_ctx));
-
- /* configure ctx save ipc message */
- pm_ctx.hdr.size = sizeof(pm_ctx);
- pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd;
-
- /* send ctx save ipc to dsp */
- return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx,
- sizeof(pm_ctx), &reply, sizeof(reply));
-}
-
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
static void sof_cache_debugfs(struct snd_sof_dev *sdev)
{
@@ -86,6 +70,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;
u32 old_state = sdev->dsp_power_state.state;
int ret;
@@ -159,20 +145,23 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
- ret = sof_set_up_pipelines(sdev, false);
- if (ret < 0) {
- dev_err(sdev->dev,
- "error: failed to restore pipeline after resume %d\n",
- ret);
- return ret;
+ if (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);
+ return ret;
+ }
}
+ /* Notify clients not managed by pm framework about core resume */
+ sof_resume_clients(sdev);
+
/* notify DSP of system resume */
- ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
- if (ret < 0)
- dev_err(sdev->dev,
- "error: ctx_restore ipc error during resume %d\n",
- ret);
+ if (pm_ops && pm_ops->ctx_restore) {
+ ret = pm_ops->ctx_restore(sdev);
+ if (ret < 0)
+ dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret);
+ }
return ret;
}
@@ -180,6 +169,9 @@ 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;
+ pm_message_t pm_state;
u32 target_state = 0;
int ret;
@@ -205,37 +197,44 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
}
target_state = snd_sof_dsp_power_target(sdev);
+ pm_state.event = target_state;
/* Skip to platform-specific suspend if DSP is entering D0 */
- if (target_state == SOF_DSP_PM_D0)
+ if (target_state == SOF_DSP_PM_D0) {
+ /* Notify clients not managed by pm framework about core suspend */
+ sof_suspend_clients(sdev, pm_state);
goto suspend;
+ }
- sof_tear_down_pipelines(sdev, false);
+ if (tplg_ops->tear_down_all_pipelines)
+ tplg_ops->tear_down_all_pipelines(sdev, false);
/* release trace */
snd_sof_release_trace(sdev);
+ /* Notify clients not managed by pm framework about core suspend */
+ sof_suspend_clients(sdev, pm_state);
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
/* cache debugfs contents during runtime suspend */
if (runtime_suspend)
sof_cache_debugfs(sdev);
#endif
/* notify DSP of upcoming power down */
- ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
- if (ret == -EBUSY || ret == -EAGAIN) {
- /*
- * runtime PM has logic to handle -EBUSY/-EAGAIN so
- * pass these errors up
- */
- dev_err(sdev->dev,
- "error: ctx_save ipc error during suspend %d\n",
- ret);
- return ret;
- } else if (ret < 0) {
- /* FW in unexpected state, continue to power down */
- dev_warn(sdev->dev,
- "ctx_save ipc error %d, proceeding with suspend\n",
- ret);
+ if (pm_ops && pm_ops->ctx_save) {
+ ret = pm_ops->ctx_save(sdev);
+ if (ret == -EBUSY || ret == -EAGAIN) {
+ /*
+ * runtime PM has logic to handle -EBUSY/-EAGAIN so
+ * pass these errors up
+ */
+ dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret);
+ return ret;
+ } else if (ret < 0) {
+ /* FW in unexpected state, continue to power down */
+ dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n",
+ ret);
+ }
}
suspend:
@@ -267,9 +266,11 @@ 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;
+
/* Notify DSP of upcoming power down */
- if (sof_ops(sdev)->remove)
- return sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
+ if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
+ return pm_ops->ctx_save(sdev);
return 0;
}
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 9e76b796502f..b2f009a0c5b7 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -27,30 +27,6 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *
return ret;
}
-static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai)
-{
- struct sof_ipc_dai_config *config;
- struct sof_ipc_reply reply;
- int ret;
-
- config = &dai->dai_config[dai->current_config];
- if (!config) {
- dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name);
- return -EINVAL;
- }
-
- /* set NONE flag to clear all previous settings */
- config->flags = SOF_DAI_CONFIG_FLAGS_NONE;
-
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
- &reply, sizeof(reply));
-
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name);
-
- return ret;
-}
-
static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct snd_sof_control *scontrol;
@@ -95,15 +71,9 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- struct sof_ipc_free ipc_free = {
- .hdr = {
- .size = sizeof(ipc_free),
- .cmd = SOF_IPC_GLB_TPLG_MSG,
- },
- .id = swidget->comp_id,
- };
- struct sof_ipc_reply reply;
- int ret, ret1, core;
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ int err = 0;
+ int ret;
if (!swidget->private)
return 0;
@@ -112,72 +82,47 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
- core = swidget->core;
-
- switch (swidget->id) {
- case snd_soc_dapm_scheduler:
- {
- const struct sof_ipc_pipe_new *pipeline = swidget->private;
-
- core = pipeline->core;
- ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;
- break;
- }
- case snd_soc_dapm_buffer:
- ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;
- break;
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- {
- struct snd_sof_dai *dai = swidget->private;
-
- dai->configured = false;
- fallthrough;
- }
- default:
- ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;
- break;
- }
-
/* continue to disable core even if IPC fails */
- ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free),
- &reply, sizeof(reply));
- if (ret < 0)
- dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
+ if (tplg_ops->widget_free)
+ err = tplg_ops->widget_free(sdev, swidget);
/*
* disable widget core. continue to route setup status and complete flag
* even if this fails and return the appropriate error
*/
- ret1 = snd_sof_dsp_core_put(sdev, core);
- if (ret1 < 0) {
+ ret = snd_sof_dsp_core_put(sdev, swidget->core);
+ if (ret < 0) {
dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
- core, swidget->widget->name);
- if (!ret)
- ret = ret1;
+ swidget->core, swidget->widget->name);
+ if (!err)
+ err = ret;
}
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
swidget->complete = 0;
- if (!ret)
+ /*
+ * free the scheduler widget (same as pipe_widget) associated with the current swidget.
+ * skip for static pipelines
+ */
+ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
+ ret = sof_widget_free(sdev, swidget->pipe_widget);
+ if (ret < 0 && !err)
+ err = ret;
+ }
+
+ if (!err)
dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
- return ret;
+ return err;
}
EXPORT_SYMBOL(sof_widget_free);
int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- struct sof_ipc_pipe_new *pipeline;
- struct sof_ipc_comp_reply r;
- struct sof_ipc_cmd_hdr *hdr;
- struct sof_ipc_comp *comp;
- struct snd_sof_dai *dai;
- size_t ipc_size;
+ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
int ret;
- int core;
/* skip if there is no private data */
if (!swidget->private)
@@ -187,74 +132,50 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (++swidget->use_count > 1)
return 0;
- /* set core ID */
- core = swidget->core;
- if (swidget->id == snd_soc_dapm_scheduler) {
- pipeline = swidget->private;
- core = pipeline->core;
+ /*
+ * The scheduler widget for a pipeline is not part of the connected DAPM
+ * widget list and it needs to be set up before the widgets in the pipeline
+ * are set up. The use_count for the scheduler widget is incremented for every
+ * widget in a given pipeline to ensure that it is freed only after the last
+ * 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);
+ ret = -EINVAL;
+ goto use_count_dec;
+ }
+
+ ret = sof_widget_setup(sdev, swidget->pipe_widget);
+ if (ret < 0)
+ goto use_count_dec;
}
/* enable widget core */
- ret = snd_sof_dsp_core_get(sdev, core);
+ ret = snd_sof_dsp_core_get(sdev, swidget->core);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
swidget->widget->name);
- goto use_count_dec;
+ goto pipe_widget_free;
}
- switch (swidget->id) {
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext);
- comp = kzalloc(ipc_size, GFP_KERNEL);
- if (!comp) {
- ret = -ENOMEM;
- goto core_put;
- }
-
- dai = swidget->private;
- dai->configured = false;
- memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai));
-
- /* append extended data to the end of the component */
- memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext,
- sizeof(swidget->comp_ext));
-
- ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r));
- kfree(comp);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to load widget %s\n",
- swidget->widget->name);
+ /* setup widget in the DSP */
+ if (tplg_ops->widget_setup) {
+ ret = tplg_ops->widget_setup(sdev, swidget);
+ if (ret < 0)
goto core_put;
- }
+ }
- ret = sof_dai_config_setup(sdev, dai);
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n",
- swidget->widget->name);
+ /* send config for DAI components */
+ if (WIDGET_IS_DAI(swidget->id)) {
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
- /*
- * widget use_count and core ref_count will both be decremented by
- * sof_widget_free()
- */
- sof_widget_free(sdev, swidget);
- return ret;
+ if (tplg_ops->dai_config) {
+ ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
+ if (ret < 0)
+ goto widget_free;
}
- break;
- case snd_soc_dapm_scheduler:
- pipeline = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
- sizeof(*pipeline), &r, sizeof(r));
- break;
- default:
- hdr = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size,
- &r, sizeof(r));
- break;
- }
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name);
- goto core_put;
}
/* restore kcontrols for widget */
@@ -262,68 +183,36 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (ret < 0) {
dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n",
swidget->widget->name);
- /*
- * widget use_count and core ref_count will both be decremented by
- * sof_widget_free()
- */
- sof_widget_free(sdev, swidget);
- return ret;
+ goto widget_free;
}
dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
return 0;
+widget_free:
+ /* widget use_count and core ref_count will both be decremented by sof_widget_free() */
+ sof_widget_free(sdev, swidget);
core_put:
- snd_sof_dsp_core_put(sdev, core);
+ 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);
use_count_dec:
swidget->use_count--;
return ret;
}
EXPORT_SYMBOL(sof_widget_setup);
-static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
-{
- struct sof_ipc_pipe_comp_connect *connect;
- struct sof_ipc_reply reply;
- int ret;
-
- /* skip if there's no private data */
- if (!sroute->private)
- return 0;
-
- /* nothing to do if route is already set up */
- if (sroute->setup)
- return 0;
-
- connect = sroute->private;
-
- dev_dbg(sdev->dev, "setting up route %s -> %s\n",
- sroute->src_widget->widget->name,
- sroute->sink_widget->widget->name);
-
- /* send ipc */
- ret = sof_ipc_tx_message(sdev->ipc,
- connect->hdr.cmd,
- connect, sizeof(*connect),
- &reply, sizeof(reply));
- if (ret < 0) {
- dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret);
- return ret;
- }
-
- sroute->setup = true;
-
- return 0;
-}
-
-static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
- struct snd_soc_dapm_widget *wsink)
+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;
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) {
@@ -357,7 +246,16 @@ static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
return -EINVAL;
}
- return sof_route_setup_ipc(sdev, sroute);
+ /* nothing to do if route is already set up */
+ if (sroute->setup)
+ return 0;
+
+ ret = ipc_tplg_ops->route_setup(sdev, sroute);
+ if (ret < 0)
+ return ret;
+
+ sroute->setup = true;
+ return 0;
}
static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
@@ -405,6 +303,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
+ const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret, num_widgets;
@@ -416,36 +315,14 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
/* set up widgets in the list */
for_each_dapm_widgets(list, num_widgets, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
- struct snd_sof_widget *pipe_widget;
if (!swidget)
continue;
- /*
- * The scheduler widget for a pipeline is not part of the connected DAPM
- * widget list and it needs to be set up before the widgets in the pipeline
- * are set up. The use_count for the scheduler widget is incremented for every
- * widget in a given pipeline to ensure that it is freed only after the last
- * widget in the pipeline is freed.
- */
- pipe_widget = swidget->pipe_widget;
- if (!pipe_widget) {
- dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
- swidget->widget->name);
- ret = -EINVAL;
- goto widget_free;
- }
-
- ret = sof_widget_setup(sdev, pipe_widget);
- if (ret < 0)
- goto widget_free;
-
/* set up the widget */
ret = sof_widget_setup(sdev, swidget);
- if (ret < 0) {
- sof_widget_free(sdev, pipe_widget);
+ if (ret < 0)
goto widget_free;
- }
}
/*
@@ -475,10 +352,12 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in
if (pipe_widget->complete)
continue;
- pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget);
- if (pipe_widget->complete < 0) {
- ret = pipe_widget->complete;
- goto widget_free;
+ 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;
+ goto widget_free;
+ }
}
}
@@ -496,7 +375,6 @@ widget_free:
break;
sof_widget_free(sdev, swidget);
- sof_widget_free(sdev, swidget->pipe_widget);
}
return ret;
@@ -530,10 +408,6 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
ret = sof_widget_free(sdev, swidget);
if (ret < 0)
ret1 = ret;
-
- ret = sof_widget_free(sdev, swidget->pipe_widget);
- if (ret < 0)
- ret1 = ret;
}
snd_soc_dapm_dai_free_widgets(&list);
@@ -624,116 +498,20 @@ int sof_set_hw_params_upon_resume(struct device *dev)
return snd_sof_dsp_hw_params_upon_resume(sdev);
}
-const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
- int pipeline_id)
-{
- const struct snd_sof_widget *swidget;
-
- list_for_each_entry(swidget, &sdev->widget_list, list)
- if (swidget->id == snd_soc_dapm_scheduler) {
- const struct sof_ipc_pipe_new *pipeline =
- swidget->private;
- if (pipeline->pipeline_id == pipeline_id)
- return pipeline;
- }
-
- return NULL;
-}
-
-int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify)
+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)
{
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
- struct snd_sof_widget *swidget;
- struct snd_sof_route *sroute;
+ const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
int ret;
- /* restore pipeline components */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- /* only set up the widgets belonging to static pipelines */
- if (!verify && swidget->dynamic_pipeline_widget)
- continue;
-
- /*
- * For older firmware, skip scheduler widgets in this loop,
- * sof_widget_setup() will be called in the 'complete pipeline' loop
- */
- if (v->abi_version < SOF_ABI_VER(3, 19, 0) &&
- swidget->id == snd_soc_dapm_scheduler)
- continue;
-
- /* update DAI config. The IPC will be sent in sof_widget_setup() */
- if (WIDGET_IS_DAI(swidget->id)) {
- struct snd_sof_dai *dai = swidget->private;
- struct sof_ipc_dai_config *config;
-
- if (!dai || !dai->dai_config)
- continue;
-
- config = dai->dai_config;
- /*
- * The link DMA channel would be invalidated for running
- * streams but not for streams that were in the PAUSED
- * state during suspend. So invalidate it here before setting
- * the dai config in the DSP.
- */
- if (config->type == SOF_DAI_INTEL_HDA)
- config->hda.link_dma_ch = DMA_CHAN_INVALID;
- }
-
- ret = sof_widget_setup(sdev, swidget);
+ /* Send PCM_FREE IPC to reset pipeline */
+ if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
}
- /* restore pipeline connections */
- list_for_each_entry(sroute, &sdev->route_list, list) {
-
- /* only set up routes belonging to static pipelines */
- if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||
- sroute->sink_widget->dynamic_pipeline_widget))
- continue;
-
- ret = sof_route_setup_ipc(sdev, sroute);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__);
- return ret;
- }
- }
-
- /* complete pipeline */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- switch (swidget->id) {
- case snd_soc_dapm_scheduler:
- /* only complete static pipelines */
- if (!verify && swidget->dynamic_pipeline_widget)
- continue;
-
- if (v->abi_version < SOF_ABI_VER(3, 19, 0)) {
- ret = sof_widget_setup(sdev, swidget);
- if (ret < 0)
- return ret;
- }
-
- swidget->complete =
- snd_sof_complete_pipeline(sdev, swidget);
- break;
- default:
- break;
- }
- }
-
- return 0;
-}
-
-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)
-{
- int ret;
-
- /* Send PCM_FREE IPC to reset pipeline */
- ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
- if (ret < 0)
- return ret;
+ spcm->prepared[substream->stream] = false;
/* stop the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
@@ -751,102 +529,6 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
}
/*
- * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that
- * did not get suspended(ex: paused streams) so the widgets can be set up again during resume.
- */
-static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
-{
- struct snd_sof_widget *swidget;
- struct snd_sof_pcm *spcm;
- int dir, ret;
-
- /*
- * free all PCMs and their associated DAPM widgets if their connected DAPM widget
- * list is not NULL. This should only be true for paused streams at this point.
- * This is equivalent to the handling of FE DAI suspend trigger for running streams.
- */
- list_for_each_entry(spcm, &sdev->pcm_list, list)
- for_each_pcm_streams(dir) {
- struct snd_pcm_substream *substream = spcm->stream[dir].substream;
-
- if (!substream || !substream->runtime)
- continue;
-
- if (spcm->stream[dir].list) {
- ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
- if (ret < 0)
- return ret;
- }
- }
-
- /*
- * free any left over DAI widgets. This is equivalent to the handling of suspend trigger
- * for the BE DAI for running streams.
- */
- list_for_each_entry(swidget, &sdev->widget_list, list)
- if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) {
- ret = sof_widget_free(sdev, swidget);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-/*
- * For older firmware, this function doesn't free widgets for static pipelines during suspend.
- * It only resets use_count for all widgets.
- */
-int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify)
-{
- struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
- struct snd_sof_widget *swidget;
- struct snd_sof_route *sroute;
- int ret;
-
- /*
- * This function is called during suspend and for one-time topology verification during
- * first boot. In both cases, there is no need to protect swidget->use_count and
- * sroute->setup because during suspend all running streams are suspended and during
- * topology loading the sound card unavailable to open PCMs.
- */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (swidget->dynamic_pipeline_widget)
- continue;
-
- /* Do not free widgets for static pipelines with FW ABI older than 3.19 */
- if (!verify && !swidget->dynamic_pipeline_widget &&
- v->abi_version < SOF_ABI_VER(3, 19, 0)) {
- swidget->use_count = 0;
- swidget->complete = 0;
- continue;
- }
-
- ret = sof_widget_free(sdev, swidget);
- if (ret < 0)
- return ret;
- }
-
- /*
- * Tear down all pipelines associated with PCMs that did not get suspended
- * and unset the prepare flag so that they can be set up again during resume.
- * Skip this step for older firmware.
- */
- if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) {
- ret = sof_tear_down_left_over_pipelines(sdev);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to tear down paused pipelines\n");
- return ret;
- }
- }
-
- list_for_each_entry(sroute, &sdev->route_list, list)
- sroute->setup = false;
-
- return 0;
-}
-
-/*
* Generic object lookup APIs.
*/
@@ -895,20 +577,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
return NULL;
}
-struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
- unsigned int pcm_id)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_sof_pcm *spcm;
-
- list_for_each_entry(spcm, &sdev->pcm_list, list) {
- if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id)
- return spcm;
- }
-
- return NULL;
-}
-
struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
const char *name)
{
@@ -960,39 +628,23 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
return NULL;
}
-#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
-#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
-
static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
{
struct snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
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;
/* use the tplg configured mclk if existed */
- if (!dai || !dai->dai_config)
+ if (!dai)
return 0;
- switch (dai->dai_config->type) {
- case SOF_DAI_INTEL_SSP:
- switch (clk_type) {
- case SOF_DAI_CLK_INTEL_SSP_MCLK:
- return dai->dai_config->ssp.mclk_rate;
- case SOF_DAI_CLK_INTEL_SSP_BCLK:
- return dai->dai_config->ssp.bclk_rate;
- default:
- dev_err(rtd->dev, "fail to get SSP clk %d rate\n",
- clk_type);
- return -EINVAL;
- }
- break;
- default:
- /* not yet implemented for platforms other than the above */
- dev_err(rtd->dev, "DAI type %d not supported yet!\n",
- dai->dai_config->type);
- return -EINVAL;
- }
+ if (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 f3009e6b91a1..7f15b3bc8196 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -30,6 +30,181 @@
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
+#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
+
+/*
+ * Volume fractional word length define to 16 sets
+ * the volume linear gain value to use Qx.16 format
+ */
+#define VOLUME_FWL 16
+
+struct snd_sof_widget;
+struct snd_sof_route;
+struct snd_sof_control;
+struct snd_sof_dai;
+
+struct snd_sof_dai_config_data {
+ int dai_index;
+ int dai_data; /* contains DAI-specific information */
+};
+
+/**
+ * struct sof_ipc_pcm_ops - IPC-specific PCM ops
+ * @hw_params: Function pointer for hw_params
+ * @hw_free: Function pointer for hw_free
+ * @trigger: Function pointer for trigger
+ * @dai_link_fixup: Function pointer for DAI link fixup
+ */
+struct sof_ipc_pcm_ops {
+ int (*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);
+ int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream);
+ 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);
+};
+
+/**
+ * struct sof_ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO
+ */
+struct sof_ipc_tplg_control_ops {
+ bool (*volume_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*volume_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ bool (*switch_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*switch_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ bool (*enum_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*enum_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*bytes_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*bytes_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol);
+ int (*bytes_ext_get)(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data, unsigned int size);
+ int (*bytes_ext_volatile_get)(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data, unsigned int size);
+ int (*bytes_ext_put)(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data, unsigned int size);
+ /* update control data based on notification from the DSP */
+ void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message);
+};
+
+/**
+ * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets
+ * @ipc_setup: Function pointer for setting up widget IPC params
+ * @ipc_free: Function pointer for freeing widget IPC params
+ * @token_list: List of token ID's that should be parsed for the widget
+ * @token_list_size: number of elements in token_list
+ * @bind_event: Function pointer for binding events to the widget
+ */
+struct sof_ipc_tplg_widget_ops {
+ int (*ipc_setup)(struct snd_sof_widget *swidget);
+ void (*ipc_free)(struct snd_sof_widget *swidget);
+ enum sof_tokens *token_list;
+ int token_list_size;
+ int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
+ u16 event_type);
+};
+
+/**
+ * struct sof_ipc_tplg_ops - IPC-specific topology ops
+ * @widget: Array of pointers to IPC-specific ops for widgets. This should always be of size
+ * SND_SOF_DAPM_TYPE_COUNT i.e one per widget type. Unsupported widget types will be
+ * initialized to 0.
+ * @control: Pointer to the IPC-specific ops for topology kcontrol IO
+ * @route_setup: Function pointer for setting up 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.
+ * @control_setup: Function pointer for setting up kcontrol IPC-specific data
+ * @control_free: Function pointer for freeing kcontrol IPC-specific data
+ * @pipeline_complete: Function pointer for pipeline complete IPC
+ * @widget_setup: Function pointer for setting up setup in the DSP
+ * @widget_free: Function pointer for freeing widget in the DSP
+ * @dai_config: Function pointer for sending DAI config IPC to the DSP
+ * @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
+ */
+struct sof_ipc_tplg_ops {
+ const struct sof_ipc_tplg_widget_ops *widget;
+ const struct sof_ipc_tplg_control_ops *control;
+ int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute);
+ const struct sof_token_info *token_list;
+ int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
+ int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
+ int (*pipeline_complete)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+ int (*widget_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+ int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+ int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+ unsigned int flags, struct snd_sof_dai_config_data *data);
+ int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type);
+ int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
+ int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
+};
+
+/** struct snd_sof_tuple - Tuple info
+ * @token: Token ID
+ * @value: union of a string or a u32 values
+ */
+struct snd_sof_tuple {
+ u32 token;
+ union {
+ u32 v;
+ const char *s;
+ } value;
+};
+
+/*
+ * List of SOF token ID's. The order of ID's does not matter as token arrays are looked up based on
+ * the ID.
+ */
+enum sof_tokens {
+ SOF_PCM_TOKENS,
+ SOF_PIPELINE_TOKENS,
+ SOF_SCHED_TOKENS,
+ SOF_ASRC_TOKENS,
+ SOF_SRC_TOKENS,
+ SOF_COMP_TOKENS,
+ SOF_BUFFER_TOKENS,
+ SOF_VOLUME_TOKENS,
+ SOF_PROCESS_TOKENS,
+ SOF_DAI_TOKENS,
+ SOF_DAI_LINK_TOKENS,
+ SOF_HDA_TOKENS,
+ SOF_SSP_TOKENS,
+ SOF_ALH_TOKENS,
+ SOF_DMIC_TOKENS,
+ SOF_DMIC_PDM_TOKENS,
+ SOF_ESAI_TOKENS,
+ SOF_SAI_TOKENS,
+ SOF_AFE_TOKENS,
+ SOF_CORE_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+
+ /* this should be the last */
+ SOF_TOKEN_COUNT,
+};
+
+/**
+ * struct sof_topology_token - SOF topology token definition
+ * @token: Token number
+ * @type: Token type
+ * @get_token: Function pointer to parse the token value and save it in a object
+ * @offset: Offset within an object to save the token value into
+ */
+struct sof_topology_token {
+ u32 token;
+ u32 type;
+ int (*get_token)(void *elem, void *object, u32 offset);
+ u32 offset;
+};
+
+struct sof_token_info {
+ const char *name;
+ const struct sof_topology_token *tokens;
+ int count;
+};
+
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
@@ -66,13 +241,20 @@ struct snd_sof_led_control {
/* ALSA SOF Kcontrol device */
struct snd_sof_control {
struct snd_soc_component *scomp;
+ const char *name;
int comp_id;
int min_volume_step; /* min volume step for volume_table */
int max_volume_step; /* max volume step for volume_table */
int num_channels;
unsigned int access;
u32 readback_offset; /* offset to mmapped data if used */
- struct sof_ipc_ctrl_data *control_data;
+ int info_type;
+ int index; /* pipeline ID */
+ void *priv; /* private data copied from topology */
+ size_t priv_size; /* size of private data */
+ size_t max_size;
+ void *ipc_control_data;
+ int max; /* applicable to volume controls */
u32 size; /* cdata size */
u32 *volume_table; /* volume table computed from tlv data*/
@@ -84,7 +266,26 @@ struct snd_sof_control {
bool comp_data_dirty;
};
-struct snd_sof_widget;
+/** struct snd_sof_dai_link - DAI link info
+ * @tuples: array of parsed tuples
+ * @num_tuples: number of tuples in the tuples array
+ * @link: Pointer to snd_soc_dai_link
+ * @hw_configs: Pointer to hw configs in topology
+ * @num_hw_configs: Number of hw configs in topology
+ * @default_hw_cfg_id: Default hw config ID
+ * @type: DAI type
+ * @list: item in snd_sof_dev dai_link list
+ */
+struct snd_sof_dai_link {
+ struct snd_sof_tuple *tuples;
+ int num_tuples;
+ struct snd_soc_dai_link *link;
+ struct snd_soc_tplg_hw_config *hw_configs;
+ int num_hw_configs;
+ int default_hw_cfg_id;
+ int type;
+ struct list_head list;
+};
/* ASoC SOF DAPM widget */
struct snd_sof_widget {
@@ -110,8 +311,10 @@ struct snd_sof_widget {
struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget;
- /* extended data for UUID components */
- struct sof_ipc_comp_ext comp_ext;
+ const guid_t uuid;
+
+ int num_tuples;
+ struct snd_sof_tuple *tuples;
void *private; /* core does not touch this */
};
@@ -134,12 +337,10 @@ struct snd_sof_dai {
struct snd_soc_component *scomp;
const char *name;
- struct sof_ipc_comp_dai comp_dai;
int number_configs;
int current_config;
- bool configured; /* DAI configured during BE hw_params */
- struct sof_ipc_dai_config *dai_config;
struct list_head list; /* list in sdev dai list */
+ void *private;
};
/*
@@ -181,8 +382,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev,
* be freed by snd_soc_unregister_component,
*/
int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file);
-int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
- struct snd_sof_widget *swidget);
/*
* Stream IPC
@@ -220,10 +419,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
unsigned int comp_id,
int *direction);
-struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp,
- unsigned int pcm_id);
-const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev,
- int pipeline_id);
void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream);
void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
@@ -244,8 +439,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set);
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */
-int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify);
-int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify);
int sof_set_hw_params_upon_resume(struct device *dev);
bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
@@ -256,6 +449,8 @@ void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
+ struct snd_soc_dapm_widget *wsink);
/* PCM */
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
@@ -264,4 +459,14 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev
struct snd_sof_pcm *spcm);
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);
+int get_token_u32(void *elem, void *object, u32 offset);
+int get_token_u16(void *elem, void *object, u32 offset);
+int get_token_comp_format(void *elem, void *object, u32 offset);
+int get_token_dai_type(void *elem, void *object, u32 offset);
+int get_token_uuid(void *elem, void *object, u32 offset);
+int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
+ struct snd_sof_tuple *tuples, int num_tuples,
+ size_t object_size, int token_instance_num);
+int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
+ struct snd_sof_pcm *spcm, int dir);
#endif
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
new file mode 100644
index 000000000000..db3a052c5dd2
--- /dev/null
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/auxiliary_bus.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <sound/sof/header.h>
+
+#include "sof-client.h"
+
+#define MAX_IPC_FLOOD_DURATION_MS 1000
+#define MAX_IPC_FLOOD_COUNT 10000
+#define IPC_FLOOD_TEST_RESULT_LEN 512
+#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
+
+#define DEBUGFS_IPC_FLOOD_COUNT "ipc_flood_count"
+#define DEBUGFS_IPC_FLOOD_DURATION "ipc_flood_duration_ms"
+
+struct sof_ipc_flood_priv {
+ struct dentry *dfs_root;
+ struct dentry *dfs_link[2];
+ char *buf;
+};
+
+static int sof_ipc_flood_dfs_open(struct inode *inode, struct file *file)
+{
+ struct sof_client_dev *cdev = inode->i_private;
+ int ret;
+
+ if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
+ return -ENODEV;
+
+ ret = debugfs_file_get(file->f_path.dentry);
+ if (unlikely(ret))
+ return ret;
+
+ ret = simple_open(inode, file);
+ if (ret)
+ debugfs_file_put(file->f_path.dentry);
+
+ return ret;
+}
+
+/*
+ * helper function to perform the flood test. Only one of the two params, ipc_duration_ms
+ * or ipc_count, will be non-zero and will determine the type of test
+ */
+static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev,
+ bool flood_duration_test,
+ unsigned long ipc_duration_ms,
+ unsigned long ipc_count)
+{
+ struct sof_ipc_flood_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ struct sof_ipc_cmd_hdr hdr;
+ struct sof_ipc_reply reply;
+ u64 min_response_time = U64_MAX;
+ ktime_t start, end, test_end;
+ u64 avg_response_time = 0;
+ u64 max_response_time = 0;
+ u64 ipc_response_time;
+ int i = 0;
+ int ret;
+
+ /* configure test IPC */
+ hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD;
+ hdr.size = sizeof(hdr);
+
+ /* set test end time for duration flood test */
+ if (flood_duration_test)
+ test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC;
+
+ /* send test IPC's */
+ while (1) {
+ start = ktime_get();
+ ret = sof_client_ipc_tx_message(cdev, &hdr, &reply, sizeof(reply));
+ end = ktime_get();
+
+ if (ret < 0)
+ break;
+
+ /* compute min and max response times */
+ ipc_response_time = ktime_to_ns(ktime_sub(end, start));
+ min_response_time = min(min_response_time, ipc_response_time);
+ max_response_time = max(max_response_time, ipc_response_time);
+
+ /* sum up response times */
+ avg_response_time += ipc_response_time;
+ i++;
+
+ /* test complete? */
+ if (flood_duration_test) {
+ if (ktime_to_ns(end) >= test_end)
+ break;
+ } else {
+ if (i == ipc_count)
+ break;
+ }
+ }
+
+ if (ret < 0)
+ dev_err(dev, "ipc flood test failed at %d iterations\n", i);
+
+ /* return if the first IPC fails */
+ if (!i)
+ return ret;
+
+ /* compute average response time */
+ do_div(avg_response_time, i);
+
+ /* clear previous test output */
+ memset(priv->buf, 0, IPC_FLOOD_TEST_RESULT_LEN);
+
+ if (!ipc_count) {
+ dev_dbg(dev, "IPC Flood test duration: %lums\n", ipc_duration_ms);
+ snprintf(priv->buf, IPC_FLOOD_TEST_RESULT_LEN,
+ "IPC Flood test duration: %lums\n", ipc_duration_ms);
+ }
+
+ dev_dbg(dev, "IPC Flood count: %d, Avg response time: %lluns\n",
+ i, avg_response_time);
+ dev_dbg(dev, "Max response time: %lluns\n", max_response_time);
+ dev_dbg(dev, "Min response time: %lluns\n", min_response_time);
+
+ /* format output string and save test results */
+ snprintf(priv->buf + strlen(priv->buf),
+ IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf),
+ "IPC Flood count: %d\nAvg response time: %lluns\n",
+ i, avg_response_time);
+
+ snprintf(priv->buf + strlen(priv->buf),
+ IPC_FLOOD_TEST_RESULT_LEN - strlen(priv->buf),
+ "Max response time: %lluns\nMin response time: %lluns\n",
+ max_response_time, min_response_time);
+
+ return ret;
+}
+
+/*
+ * Writing to the debugfs entry initiates the IPC flood test based on
+ * the IPC count or the duration specified by the user.
+ */
+static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct device *dev = &cdev->auxdev.dev;
+ unsigned long ipc_duration_ms = 0;
+ bool flood_duration_test = false;
+ unsigned long ipc_count = 0;
+ struct dentry *dentry;
+ int err;
+ size_t size;
+ char *string;
+ int ret;
+
+ string = kzalloc(count + 1, GFP_KERNEL);
+ if (!string)
+ return -ENOMEM;
+
+ size = simple_write_to_buffer(string, count, ppos, buffer, count);
+
+ /*
+ * write op is only supported for ipc_flood_count or
+ * ipc_flood_duration_ms debugfs entries atm.
+ * ipc_flood_count floods the DSP with the number of IPC's specified.
+ * ipc_duration_ms test floods the DSP for the time specified
+ * in the debugfs entry.
+ */
+ dentry = file->f_path.dentry;
+ if (strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) &&
+ strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION))
+ flood_duration_test = true;
+
+ /* test completion criterion */
+ if (flood_duration_test)
+ ret = kstrtoul(string, 0, &ipc_duration_ms);
+ else
+ ret = kstrtoul(string, 0, &ipc_count);
+ if (ret < 0)
+ goto out;
+
+ /* limit max duration/ipc count for flood test */
+ if (flood_duration_test) {
+ if (!ipc_duration_ms) {
+ ret = size;
+ goto out;
+ }
+
+ /* find the minimum. min() is not used to avoid warnings */
+ if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS)
+ ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS;
+ } else {
+ if (!ipc_count) {
+ ret = size;
+ goto out;
+ }
+
+ /* find the minimum. min() is not used to avoid warnings */
+ if (ipc_count > MAX_IPC_FLOOD_COUNT)
+ ipc_count = MAX_IPC_FLOOD_COUNT;
+ }
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ goto out;
+ }
+
+ /* flood test */
+ ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
+ ipc_duration_ms, ipc_count);
+
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_put_autosuspend(dev);
+ if (err < 0)
+ dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
+
+ /* return size if test is successful */
+ if (ret >= 0)
+ ret = size;
+out:
+ kfree(string);
+ return ret;
+}
+
+/* return the result of the last IPC flood test */
+static ssize_t sof_ipc_flood_dfs_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_ipc_flood_priv *priv = cdev->data;
+ size_t size_ret;
+
+ struct dentry *dentry;
+
+ dentry = file->f_path.dentry;
+ if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) ||
+ !strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) {
+ if (*ppos)
+ return 0;
+
+ count = min_t(size_t, count, strlen(priv->buf));
+ size_ret = copy_to_user(buffer, priv->buf, count);
+ if (size_ret)
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+ }
+ return count;
+}
+
+static int sof_ipc_flood_dfs_release(struct inode *inode, struct file *file)
+{
+ debugfs_file_put(file->f_path.dentry);
+
+ return 0;
+}
+
+static const struct file_operations sof_ipc_flood_fops = {
+ .open = sof_ipc_flood_dfs_open,
+ .read = sof_ipc_flood_dfs_read,
+ .llseek = default_llseek,
+ .write = sof_ipc_flood_dfs_write,
+ .release = sof_ipc_flood_dfs_release,
+
+ .owner = THIS_MODULE,
+};
+
+/*
+ * The IPC test client creates a couple of debugfs entries that will be used
+ * flood tests. Users can write to these entries to execute the IPC flood test
+ * by specifying either the number of IPCs to flood the DSP with or the duration
+ * (in ms) for which the DSP should be flooded with test IPCs. At the
+ * end of each test, the average, min and max response times are reported back.
+ * The results of the last flood test can be accessed by reading the debugfs
+ * entries.
+ */
+static int sof_ipc_flood_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
+ struct device *dev = &auxdev->dev;
+ struct sof_ipc_flood_priv *priv;
+
+ /* allocate memory for client data */
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->buf = devm_kmalloc(dev, IPC_FLOOD_TEST_RESULT_LEN, GFP_KERNEL);
+ if (!priv->buf)
+ return -ENOMEM;
+
+ cdev->data = priv;
+
+ /* create debugfs root folder with device name under parent SOF dir */
+ priv->dfs_root = debugfs_create_dir(dev_name(dev), debugfs_root);
+ if (!IS_ERR_OR_NULL(priv->dfs_root)) {
+ /* create read-write ipc_flood_count debugfs entry */
+ debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, priv->dfs_root,
+ cdev, &sof_ipc_flood_fops);
+
+ /* create read-write ipc_flood_duration_ms debugfs entry */
+ debugfs_create_file(DEBUGFS_IPC_FLOOD_DURATION, 0644,
+ priv->dfs_root, cdev, &sof_ipc_flood_fops);
+
+ if (auxdev->id == 0) {
+ /*
+ * Create symlinks for backwards compatibility to the
+ * first IPC flood test instance
+ */
+ char target[100];
+
+ snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_COUNT,
+ dev_name(dev));
+ priv->dfs_link[0] =
+ debugfs_create_symlink(DEBUGFS_IPC_FLOOD_COUNT,
+ debugfs_root, target);
+
+ snprintf(target, 100, "%s/" DEBUGFS_IPC_FLOOD_DURATION,
+ dev_name(dev));
+ priv->dfs_link[1] =
+ debugfs_create_symlink(DEBUGFS_IPC_FLOOD_DURATION,
+ debugfs_root, target);
+ }
+ }
+
+ /* enable runtime PM */
+ pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void sof_ipc_flood_remove(struct auxiliary_device *auxdev)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct sof_ipc_flood_priv *priv = cdev->data;
+
+ pm_runtime_disable(&auxdev->dev);
+
+ if (auxdev->id == 0) {
+ debugfs_remove(priv->dfs_link[0]);
+ debugfs_remove(priv->dfs_link[1]);
+ }
+
+ debugfs_remove_recursive(priv->dfs_root);
+}
+
+static const struct auxiliary_device_id sof_ipc_flood_client_id_table[] = {
+ { .name = "snd_sof.ipc_flood" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, sof_ipc_flood_client_id_table);
+
+/*
+ * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
+ * type are enough to ensure that the parent SOF device resumes to bring the DSP
+ * back to D0.
+ * Driver name will be set based on KBUILD_MODNAME.
+ */
+static struct auxiliary_driver sof_ipc_flood_client_drv = {
+ .probe = sof_ipc_flood_probe,
+ .remove = sof_ipc_flood_remove,
+
+ .id_table = sof_ipc_flood_client_id_table,
+};
+
+module_auxiliary_driver(sof_ipc_flood_client_drv);
+
+MODULE_DESCRIPTION("SOF IPC Flood Test Client Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
new file mode 100644
index 000000000000..dba6cfd7db09
--- /dev/null
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Author: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/auxiliary_bus.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <sound/sof/header.h>
+
+#include "sof-client.h"
+
+#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
+
+struct sof_msg_inject_priv {
+ struct dentry *dfs_file;
+
+ void *tx_buffer;
+ void *rx_buffer;
+};
+
+static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file)
+{
+ struct sof_client_dev *cdev = inode->i_private;
+ int ret;
+
+ if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
+ return -ENODEV;
+
+ ret = debugfs_file_get(file->f_path.dentry);
+ if (unlikely(ret))
+ return ret;
+
+ ret = simple_open(inode, file);
+ if (ret)
+ debugfs_file_put(file->f_path.dentry);
+
+ return ret;
+}
+
+static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_msg_inject_priv *priv = cdev->data;
+ struct sof_ipc_reply *rhdr = priv->rx_buffer;
+
+ if (!rhdr->hdr.size || !count || *ppos)
+ return 0;
+
+ if (count > rhdr->hdr.size)
+ count = rhdr->hdr.size;
+
+ if (copy_to_user(buffer, priv->rx_buffer, count))
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+}
+
+static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_msg_inject_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ int ret, err;
+ size_t size;
+
+ if (*ppos)
+ return 0;
+
+ size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE,
+ ppos, buffer, count);
+ if (size != count)
+ return size > 0 ? -EFAULT : size;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
+ /* send the message */
+ memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE);
+ ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
+ SOF_IPC_MSG_MAX_SIZE);
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_put_autosuspend(dev);
+ if (err < 0)
+ dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
+
+ /* return size if test is successful */
+ if (ret >= 0)
+ ret = size;
+
+ return ret;
+};
+
+static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
+{
+ debugfs_file_put(file->f_path.dentry);
+
+ return 0;
+}
+
+static const struct file_operations sof_msg_inject_fops = {
+ .open = sof_msg_inject_dfs_open,
+ .read = sof_msg_inject_dfs_read,
+ .write = sof_msg_inject_dfs_write,
+ .llseek = default_llseek,
+ .release = sof_msg_inject_dfs_release,
+
+ .owner = THIS_MODULE,
+};
+
+static int sof_msg_inject_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
+ struct device *dev = &auxdev->dev;
+ struct sof_msg_inject_priv *priv;
+
+ /* allocate memory for client data */
+ priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ priv->rx_buffer = devm_kzalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!priv->tx_buffer || !priv->rx_buffer)
+ return -ENOMEM;
+
+ cdev->data = priv;
+
+ priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root,
+ cdev, &sof_msg_inject_fops);
+
+ /* enable runtime PM */
+ pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void sof_msg_inject_remove(struct auxiliary_device *auxdev)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct sof_msg_inject_priv *priv = cdev->data;
+
+ pm_runtime_disable(&auxdev->dev);
+
+ debugfs_remove(priv->dfs_file);
+}
+
+static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = {
+ { .name = "snd_sof.msg_injector" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table);
+
+/*
+ * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
+ * type are enough to ensure that the parent SOF device resumes to bring the DSP
+ * back to D0.
+ * Driver name will be set based on KBUILD_MODNAME.
+ */
+static struct auxiliary_driver sof_msg_inject_client_drv = {
+ .probe = sof_msg_inject_probe,
+ .remove = sof_msg_inject_remove,
+
+ .id_table = sof_msg_inject_client_id_table,
+};
+
+module_auxiliary_driver(sof_msg_inject_client_drv);
+
+MODULE_DESCRIPTION("SOF IPC Message Injector Client Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
new file mode 100644
index 000000000000..797dedb26163
--- /dev/null
+++ b/sound/soc/sof/sof-client-probes.c
@@ -0,0 +1,821 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2019-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+// SOF client support:
+// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/sof/header.h>
+#include "sof-client.h"
+#include "sof-client-probes.h"
+
+#define SOF_PROBES_SUSPEND_DELAY_MS 3000
+/* only extraction supported for now */
+#define SOF_PROBES_NUM_DAI_LINKS 1
+
+#define SOF_PROBES_INVALID_NODE_ID UINT_MAX
+
+static bool __read_mostly sof_probes_enabled;
+module_param_named(enable, sof_probes_enabled, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable SOF probes support");
+
+struct sof_probes_priv {
+ struct dentry *dfs_points;
+ struct dentry *dfs_points_remove;
+ u32 extractor_stream_tag;
+ struct snd_soc_card card;
+
+ const struct sof_probes_host_ops *host_ops;
+};
+
+struct sof_probe_point_desc {
+ unsigned int buffer_id;
+ unsigned int purpose;
+ unsigned int stream_tag;
+} __packed;
+
+struct sof_probe_dma {
+ unsigned int stream_tag;
+ unsigned int dma_buffer_size;
+} __packed;
+
+struct sof_ipc_probe_dma_add_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ struct sof_probe_dma dma[];
+} __packed;
+
+struct sof_ipc_probe_info_params {
+ struct sof_ipc_reply rhdr;
+ unsigned int num_elems;
+ union {
+ struct sof_probe_dma dma[0];
+ struct sof_probe_point_desc desc[0];
+ };
+} __packed;
+
+struct sof_ipc_probe_point_add_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ struct sof_probe_point_desc desc[];
+} __packed;
+
+struct sof_ipc_probe_point_remove_params {
+ struct sof_ipc_cmd_hdr hdr;
+ unsigned int num_elems;
+ unsigned int buffer_id[];
+} __packed;
+
+/**
+ * sof_probes_init - initialize data probing
+ * @cdev: SOF client device
+ * @stream_tag: Extractor stream tag
+ * @buffer_size: DMA buffer size to set for extractor
+ *
+ * Host chooses whether extraction is supported or not by providing
+ * valid stream tag to DSP. Once specified, stream described by that
+ * tag will be tied to DSP for extraction for the entire lifetime of
+ * probe.
+ *
+ * Probing is initialized only once and each INIT request must be
+ * matched by DEINIT call.
+ */
+static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
+ size_t buffer_size)
+{
+ struct sof_ipc_probe_dma_add_params *msg;
+ size_t size = struct_size(msg, dma, 1);
+ struct sof_ipc_reply reply;
+ int ret;
+
+ msg = kmalloc(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ msg->hdr.size = size;
+ msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT;
+ msg->num_elems = 1;
+ msg->dma[0].stream_tag = stream_tag;
+ msg->dma[0].dma_buffer_size = buffer_size;
+
+ ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ kfree(msg);
+ return ret;
+}
+
+/**
+ * sof_probes_deinit - cleanup after data probing
+ * @cdev: SOF client device
+ *
+ * Host sends DEINIT request to free previously initialized probe
+ * on DSP side once it is no longer needed. DEINIT only when there
+ * are no probes connected and with all injectors detached.
+ */
+static int sof_probes_deinit(struct sof_client_dev *cdev)
+{
+ struct sof_ipc_cmd_hdr msg;
+ struct sof_ipc_reply reply;
+
+ msg.size = sizeof(msg);
+ msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
+
+ return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
+}
+
+static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
+ void **params, size_t *num_params)
+{
+ struct sof_ipc_probe_info_params msg = {{{0}}};
+ struct sof_ipc_probe_info_params *reply;
+ size_t bytes;
+ int ret;
+
+ *params = NULL;
+ *num_params = 0;
+
+ reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!reply)
+ return -ENOMEM;
+ msg.rhdr.hdr.size = sizeof(msg);
+ msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
+
+ ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE);
+ if (ret < 0 || reply->rhdr.error < 0)
+ goto exit;
+
+ if (!reply->num_elems)
+ goto exit;
+
+ if (cmd == SOF_IPC_PROBE_DMA_INFO)
+ bytes = sizeof(reply->dma[0]);
+ else
+ bytes = sizeof(reply->desc[0]);
+ bytes *= reply->num_elems;
+ *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
+ if (!*params) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ *num_params = reply->num_elems;
+
+exit:
+ kfree(reply);
+ return ret;
+}
+
+/**
+ * sof_probes_points_info - retrieve list of active probe points
+ * @cdev: SOF client device
+ * @desc: Returned list of active probes
+ * @num_desc: Returned count of active probes
+ *
+ * Host sends PROBE_POINT_INFO request to obtain list of active probe
+ * points, valid for disconnection when given probe is no longer
+ * required.
+ */
+static int sof_probes_points_info(struct sof_client_dev *cdev,
+ struct sof_probe_point_desc **desc,
+ size_t *num_desc)
+{
+ return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
+ (void **)desc, num_desc);
+}
+
+/**
+ * sof_probes_points_add - connect specified probes
+ * @cdev: SOF client device
+ * @desc: List of probe points to connect
+ * @num_desc: Number of elements in @desc
+ *
+ * Dynamically connects to provided set of endpoints. Immediately
+ * after connection is established, host must be prepared to
+ * transfer data from or to target stream given the probing purpose.
+ *
+ * Each probe point should be removed using PROBE_POINT_REMOVE
+ * request when no longer needed.
+ */
+static int sof_probes_points_add(struct sof_client_dev *cdev,
+ struct sof_probe_point_desc *desc,
+ size_t num_desc)
+{
+ struct sof_ipc_probe_point_add_params *msg;
+ size_t size = struct_size(msg, desc, num_desc);
+ struct sof_ipc_reply reply;
+ int ret;
+
+ msg = kmalloc(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ msg->hdr.size = size;
+ msg->num_elems = num_desc;
+ msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
+ memcpy(&msg->desc[0], desc, size - sizeof(*msg));
+
+ ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ kfree(msg);
+ return ret;
+}
+
+/**
+ * sof_probes_points_remove - disconnect specified probes
+ * @cdev: SOF client device
+ * @buffer_id: List of probe points to disconnect
+ * @num_buffer_id: Number of elements in @desc
+ *
+ * Removes previously connected probes from list of active probe
+ * points and frees all resources on DSP side.
+ */
+static int sof_probes_points_remove(struct sof_client_dev *cdev,
+ unsigned int *buffer_id, size_t num_buffer_id)
+{
+ struct sof_ipc_probe_point_remove_params *msg;
+ size_t size = struct_size(msg, buffer_id, num_buffer_id);
+ struct sof_ipc_reply reply;
+ int ret;
+
+ msg = kmalloc(size, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ msg->hdr.size = size;
+ msg->num_elems = num_buffer_id;
+ msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
+ memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
+
+ ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ kfree(msg);
+ return ret;
+}
+
+static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
+ struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
+ struct sof_probes_priv *priv = cdev->data;
+ const struct sof_probes_host_ops *ops = priv->host_ops;
+ int ret;
+
+ if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
+ return -ENODEV;
+
+ ret = sof_client_core_module_get(cdev);
+ if (ret)
+ return ret;
+
+ ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag);
+ if (ret) {
+ dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
+ priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
+ sof_client_core_module_put(cdev);
+ }
+
+ return ret;
+}
+
+static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
+ struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
+ struct sof_probes_priv *priv = cdev->data;
+ const struct sof_probes_host_ops *ops = priv->host_ops;
+ struct sof_probe_point_desc *desc;
+ size_t num_desc;
+ int i, ret;
+
+ /* disconnect all probe points */
+ ret = sof_probes_points_info(cdev, &desc, &num_desc);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
+ goto exit;
+ }
+
+ for (i = 0; i < num_desc; i++)
+ sof_probes_points_remove(cdev, &desc[i].buffer_id, 1);
+ kfree(desc);
+
+exit:
+ ret = sof_probes_deinit(cdev);
+ if (ret < 0)
+ dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
+
+ priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
+ snd_compr_free_pages(cstream);
+
+ ret = ops->free(cdev, cstream, dai);
+
+ sof_client_core_module_put(cdev);
+
+ return ret;
+}
+
+static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
+ struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ struct sof_probes_priv *priv = cdev->data;
+ const struct sof_probes_host_ops *ops = priv->host_ops;
+ int ret;
+
+ cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
+ cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
+ ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
+ if (ret < 0)
+ return ret;
+
+ ret = ops->set_params(cdev, cstream, params, dai);
+ if (ret)
+ return ret;
+
+ ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to init probe: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
+ struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
+ struct sof_probes_priv *priv = cdev->data;
+ const struct sof_probes_host_ops *ops = priv->host_ops;
+
+ return ops->trigger(cdev, cstream, cmd, dai);
+}
+
+static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
+ struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
+ struct sof_probes_priv *priv = cdev->data;
+ const struct sof_probes_host_ops *ops = priv->host_ops;
+
+ return ops->pointer(cdev, cstream, tstamp, dai);
+}
+
+static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
+ .startup = sof_probes_compr_startup,
+ .shutdown = sof_probes_compr_shutdown,
+ .set_params = sof_probes_compr_set_params,
+ .trigger = sof_probes_compr_trigger,
+ .pointer = sof_probes_compr_pointer,
+};
+
+static int sof_probes_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *rtd = cstream->runtime;
+ unsigned int offset, n;
+ void *ptr;
+ int ret;
+
+ if (count > rtd->buffer_size)
+ count = rtd->buffer_size;
+
+ div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
+ ptr = rtd->dma_area + offset;
+ n = rtd->buffer_size - offset;
+
+ if (count < n) {
+ ret = copy_to_user(buf, ptr, count);
+ } else {
+ ret = copy_to_user(buf, ptr, n);
+ ret += copy_to_user(buf + n, rtd->dma_area, count - n);
+ }
+
+ if (ret)
+ return count - ret;
+ return count;
+}
+
+static const struct snd_compress_ops sof_probes_compressed_ops = {
+ .copy = sof_probes_compr_copy,
+};
+
+/**
+ * strsplit_u32 - Split string into sequence of u32 tokens
+ * @buf: String to split into tokens.
+ * @delim: String containing delimiter characters.
+ * @tkns: Returned u32 sequence pointer.
+ * @num_tkns: Returned number of tokens obtained.
+ */
+static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns)
+{
+ char *s;
+ u32 *data, *tmp;
+ size_t count = 0;
+ size_t cap = 32;
+ int ret = 0;
+
+ *tkns = NULL;
+ *num_tkns = 0;
+ data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ while ((s = strsep(&buf, delim)) != NULL) {
+ ret = kstrtouint(s, 0, data + count);
+ if (ret)
+ goto exit;
+ if (++count >= cap) {
+ cap *= 2;
+ tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
+ if (!tmp) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ data = tmp;
+ }
+ }
+
+ if (!count)
+ goto exit;
+ *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
+ if (!(*tkns)) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ *num_tkns = count;
+
+exit:
+ kfree(data);
+ return ret;
+}
+
+static int tokenize_input(const char __user *from, size_t count,
+ loff_t *ppos, u32 **tkns, size_t *num_tkns)
+{
+ char *buf;
+ int ret;
+
+ buf = kmalloc(count + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = simple_write_to_buffer(buf, count, ppos, from, count);
+ if (ret != count) {
+ ret = ret >= 0 ? -EIO : ret;
+ goto exit;
+ }
+
+ buf[count] = '\0';
+ ret = strsplit_u32(buf, ",", tkns, num_tkns);
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_probes_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ struct sof_probe_point_desc *desc;
+ int remaining, offset;
+ size_t num_desc;
+ char *buf;
+ int i, ret, err;
+
+ if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
+ dev_warn(dev, "no extractor stream running\n");
+ return -ENOENT;
+ }
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ goto exit;
+ }
+
+ ret = sof_probes_points_info(cdev, &desc, &num_desc);
+ if (ret < 0)
+ goto exit;
+
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_put_autosuspend(dev);
+ if (err < 0)
+ dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
+
+ for (i = 0; i < num_desc; i++) {
+ offset = strlen(buf);
+ remaining = PAGE_SIZE - offset;
+ ret = snprintf(buf + offset, remaining,
+ "Id: %#010x Purpose: %u Node id: %#x\n",
+ desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
+ if (ret < 0 || ret >= remaining) {
+ /* truncate the output buffer at the last full line */
+ buf[offset] = '\0';
+ break;
+ }
+ }
+
+ ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
+
+ kfree(desc);
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t
+sof_probes_dfs_points_write(struct file *file, const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_probes_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ struct sof_probe_point_desc *desc;
+ size_t num_tkns, bytes;
+ u32 *tkns;
+ int ret, err;
+
+ if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
+ dev_warn(dev, "no extractor stream running\n");
+ return -ENOENT;
+ }
+
+ ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
+ if (ret < 0)
+ return ret;
+ bytes = sizeof(*tkns) * num_tkns;
+ if (!num_tkns || (bytes % sizeof(*desc))) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ desc = (struct sof_probe_point_desc *)tkns;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ goto exit;
+ }
+
+ ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc));
+ if (!ret)
+ ret = count;
+
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_put_autosuspend(dev);
+ if (err < 0)
+ dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
+exit:
+ kfree(tkns);
+ return ret;
+}
+
+static const struct file_operations sof_probes_points_fops = {
+ .open = simple_open,
+ .read = sof_probes_dfs_points_read,
+ .write = sof_probes_dfs_points_write,
+ .llseek = default_llseek,
+
+ .owner = THIS_MODULE,
+};
+
+static ssize_t
+sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct sof_client_dev *cdev = file->private_data;
+ struct sof_probes_priv *priv = cdev->data;
+ struct device *dev = &cdev->auxdev.dev;
+ size_t num_tkns;
+ u32 *tkns;
+ int ret, err;
+
+ if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
+ dev_warn(dev, "no extractor stream running\n");
+ return -ENOENT;
+ }
+
+ ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
+ if (ret < 0)
+ return ret;
+ if (!num_tkns) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ goto exit;
+ }
+
+ ret = sof_probes_points_remove(cdev, tkns, num_tkns);
+ if (!ret)
+ ret = count;
+
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_put_autosuspend(dev);
+ if (err < 0)
+ dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
+exit:
+ kfree(tkns);
+ return ret;
+}
+
+static const struct file_operations sof_probes_points_remove_fops = {
+ .open = simple_open,
+ .write = sof_probes_dfs_points_remove_write,
+ .llseek = default_llseek,
+
+ .owner = THIS_MODULE,
+};
+
+static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
+{
+ .name = "Probe Extraction CPU DAI",
+ .compress_new = snd_soc_new_compress,
+ .cops = &sof_probes_compr_ops,
+ .capture = {
+ .stream_name = "Probe Extraction",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+},
+};
+
+static const struct snd_soc_component_driver sof_probes_component = {
+ .name = "sof-probes-component",
+ .compress_ops = &sof_probes_compressed_ops,
+ .module_get_upon_open = 1,
+};
+
+SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
+
+static int sof_probes_client_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
+ struct device *dev = &auxdev->dev;
+ struct snd_soc_dai_link_component platform_component[] = {
+ {
+ .name = dev_name(dev),
+ }
+ };
+ struct snd_soc_card *card;
+ struct sof_probes_priv *priv;
+ struct snd_soc_dai_link_component *cpus;
+ struct sof_probes_host_ops *ops;
+ struct snd_soc_dai_link *links;
+ int ret;
+
+ /* do not set up the probes support if it is not enabled */
+ if (!sof_probes_enabled)
+ return -ENXIO;
+
+ if (!dev->platform_data) {
+ dev_err(dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ops = dev->platform_data;
+
+ if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger ||
+ !ops->pointer) {
+ dev_err(dev, "missing platform callback(s)\n");
+ return -ENODEV;
+ }
+
+ priv->host_ops = ops;
+ cdev->data = priv;
+
+ /* register probes component driver and dai */
+ ret = devm_snd_soc_register_component(dev, &sof_probes_component,
+ sof_probes_dai_drv,
+ ARRAY_SIZE(sof_probes_dai_drv));
+ if (ret < 0) {
+ dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
+ return ret;
+ }
+
+ /* set client data */
+ priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
+
+ /* create read-write probes_points debugfs entry */
+ priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
+ cdev, &sof_probes_points_fops);
+
+ /* create read-write probe_points_remove debugfs entry */
+ priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
+ dfsroot, cdev,
+ &sof_probes_points_remove_fops);
+
+ links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
+ cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
+ if (!links || !cpus) {
+ debugfs_remove(priv->dfs_points);
+ debugfs_remove(priv->dfs_points_remove);
+ return -ENOMEM;
+ }
+
+ /* extraction DAI link */
+ links[0].name = "Compress Probe Capture";
+ links[0].id = 0;
+ links[0].cpus = &cpus[0];
+ links[0].num_cpus = 1;
+ links[0].cpus->dai_name = "Probe Extraction CPU DAI";
+ links[0].codecs = dummy;
+ links[0].num_codecs = 1;
+ links[0].platforms = platform_component;
+ links[0].num_platforms = ARRAY_SIZE(platform_component);
+ links[0].nonatomic = 1;
+
+ card = &priv->card;
+
+ card->dev = dev;
+ card->name = "sof-probes";
+ card->owner = THIS_MODULE;
+ card->num_links = SOF_PROBES_NUM_DAI_LINKS;
+ card->dai_link = links;
+
+ /* set idle_bias_off to prevent the core from resuming the card->dev */
+ card->dapm.idle_bias_off = true;
+
+ snd_soc_card_set_drvdata(card, cdev);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ debugfs_remove(priv->dfs_points);
+ debugfs_remove(priv->dfs_points_remove);
+ dev_err(dev, "Probes card register failed %d\n", ret);
+ return ret;
+ }
+
+ /* enable runtime PM */
+ pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void sof_probes_client_remove(struct auxiliary_device *auxdev)
+{
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+ struct sof_probes_priv *priv = cdev->data;
+
+ if (!sof_probes_enabled)
+ return;
+
+ pm_runtime_disable(&auxdev->dev);
+ debugfs_remove(priv->dfs_points);
+ debugfs_remove(priv->dfs_points_remove);
+}
+
+static const struct auxiliary_device_id sof_probes_client_id_table[] = {
+ { .name = "snd_sof.hda-probes", },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
+
+/* driver name will be set based on KBUILD_MODNAME */
+static struct auxiliary_driver sof_probes_client_drv = {
+ .probe = sof_probes_client_probe,
+ .remove = sof_probes_client_remove,
+
+ .id_table = sof_probes_client_id_table,
+};
+
+module_auxiliary_driver(sof_probes_client_drv);
+
+MODULE_DESCRIPTION("SOF Probes Client Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/sof-client-probes.h b/sound/soc/sof/sof-client-probes.h
new file mode 100644
index 000000000000..0f9ed4569fd3
--- /dev/null
+++ b/sound/soc/sof/sof-client-probes.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOF_CLIENT_PROBES_H
+#define __SOF_CLIENT_PROBES_H
+
+struct snd_compr_stream;
+struct snd_compr_tstamp;
+struct snd_compr_params;
+struct sof_client_dev;
+struct snd_soc_dai;
+
+/*
+ * Callbacks used on platforms where the control for audio is split between
+ * DSP and host, like HDA.
+ */
+struct sof_probes_host_ops {
+ int (*assign)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai, u32 *stream_id);
+ int (*free)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
+ struct snd_soc_dai *dai);
+ int (*set_params)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ struct snd_soc_dai *dai);
+ int (*trigger)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
+ int cmd, struct snd_soc_dai *dai);
+ int (*pointer)(struct sof_client_dev *cdev, struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp,
+ struct snd_soc_dai *dai);
+};
+
+#endif
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
new file mode 100644
index 000000000000..686ad0c3bb61
--- /dev/null
+++ b/sound/soc/sof/sof-client.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "ops.h"
+#include "sof-client.h"
+#include "sof-priv.h"
+
+/**
+ * struct sof_ipc_event_entry - IPC client event description
+ * @ipc_msg_type: IPC msg type of the event the client is interested
+ * @cdev: sof_client_dev of the requesting client
+ * @callback: Callback function of the client
+ * @list: item in SOF core client event list
+ */
+struct sof_ipc_event_entry {
+ u32 ipc_msg_type;
+ struct sof_client_dev *cdev;
+ sof_client_event_callback callback;
+ struct list_head list;
+};
+
+/**
+ * struct sof_state_event_entry - DSP panic event subscription entry
+ * @cdev: sof_client_dev of the requesting client
+ * @callback: Callback function of the client
+ * @list: item in SOF core client event list
+ */
+struct sof_state_event_entry {
+ struct sof_client_dev *cdev;
+ sof_client_fw_state_callback callback;
+ struct list_head list;
+};
+
+static void sof_client_auxdev_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+
+ kfree(cdev->auxdev.dev.platform_data);
+ kfree(cdev);
+}
+
+static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
+ size_t size)
+{
+ void *d = NULL;
+
+ if (data) {
+ d = kmemdup(data, size, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+ }
+
+ cdev->auxdev.dev.platform_data = d;
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)
+static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) {
+ ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0);
+ if (ret < 0)
+ break;
+ }
+
+ if (ret) {
+ for (; i >= 0; --i)
+ sof_client_dev_unregister(sdev, "ipc_flood", i);
+ }
+
+ return ret;
+}
+
+static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++)
+ sof_client_dev_unregister(sdev, "ipc_flood", i);
+}
+#else
+static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {}
+#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
+{
+ return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0);
+}
+
+static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev)
+{
+ sof_client_dev_unregister(sdev, "msg_injector", 0);
+}
+#else
+static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {}
+#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */
+
+int sof_register_clients(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ /* Register platform independent client devices */
+ ret = sof_register_ipc_flood_test(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "IPC flood test client registration failed\n");
+ return ret;
+ }
+
+ ret = sof_register_ipc_msg_injector(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "IPC message injector client registration failed\n");
+ goto err_msg_injector;
+ }
+
+ /* Platform depndent client device registration */
+
+ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
+ ret = sof_ops(sdev)->register_ipc_clients(sdev);
+
+ if (!ret)
+ return 0;
+
+ sof_unregister_ipc_msg_injector(sdev);
+
+err_msg_injector:
+ sof_unregister_ipc_flood_test(sdev);
+
+ return ret;
+}
+
+void sof_unregister_clients(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
+ sof_ops(sdev)->unregister_ipc_clients(sdev);
+
+ sof_unregister_ipc_msg_injector(sdev);
+ sof_unregister_ipc_flood_test(sdev);
+}
+
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
+ const void *data, size_t size)
+{
+ struct auxiliary_device *auxdev;
+ struct sof_client_dev *cdev;
+ int ret;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->sdev = sdev;
+ auxdev = &cdev->auxdev;
+ auxdev->name = name;
+ auxdev->dev.parent = sdev->dev;
+ auxdev->dev.release = sof_client_auxdev_release;
+ auxdev->id = id;
+
+ ret = sof_client_dev_add_data(cdev, data, size);
+ if (ret < 0)
+ goto err_dev_add_data;
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id);
+ goto err_dev_init;
+ }
+
+ ret = auxiliary_device_add(&cdev->auxdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id);
+ /*
+ * sof_client_auxdev_release() will be invoked to free up memory
+ * allocations through put_device()
+ */
+ auxiliary_device_uninit(&cdev->auxdev);
+ return ret;
+ }
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->ipc_client_mutex);
+ list_add(&cdev->list, &sdev->ipc_client_list);
+ mutex_unlock(&sdev->ipc_client_mutex);
+
+ return 0;
+
+err_dev_init:
+ kfree(cdev->auxdev.dev.platform_data);
+
+err_dev_add_data:
+ kfree(cdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT);
+
+void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
+{
+ struct sof_client_dev *cdev;
+
+ mutex_lock(&sdev->ipc_client_mutex);
+
+ /*
+ * sof_client_auxdev_release() will be invoked to free up memory
+ * allocations through put_device()
+ */
+ list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
+ list_del(&cdev->list);
+ auxiliary_device_delete(&cdev->auxdev);
+ auxiliary_device_uninit(&cdev->auxdev);
+ break;
+ }
+ }
+
+ mutex_unlock(&sdev->ipc_client_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
+ void *reply_data, size_t reply_bytes)
+{
+ struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+
+ return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size,
+ reply_data, reply_bytes);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
+
+int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
+{
+ struct auxiliary_driver *adrv;
+ struct sof_client_dev *cdev;
+
+ mutex_lock(&sdev->ipc_client_mutex);
+
+ list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ /* Skip devices without loaded driver */
+ if (!cdev->auxdev.dev.driver)
+ continue;
+
+ adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
+ if (adrv->suspend)
+ adrv->suspend(&cdev->auxdev, state);
+ }
+
+ mutex_unlock(&sdev->ipc_client_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, SND_SOC_SOF_CLIENT);
+
+int sof_resume_clients(struct snd_sof_dev *sdev)
+{
+ struct auxiliary_driver *adrv;
+ struct sof_client_dev *cdev;
+
+ mutex_lock(&sdev->ipc_client_mutex);
+
+ list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ /* Skip devices without loaded driver */
+ if (!cdev->auxdev.dev.driver)
+ continue;
+
+ adrv = to_auxiliary_drv(cdev->auxdev.dev.driver);
+ if (adrv->resume)
+ adrv->resume(&cdev->auxdev);
+ }
+
+ mutex_unlock(&sdev->ipc_client_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_resume_clients, SND_SOC_SOF_CLIENT);
+
+struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
+{
+ return cdev->sdev->debugfs_root;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT);
+
+/* DMA buffer allocation in client drivers must use the core SOF device */
+struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
+{
+ return cdev->sdev->dev;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT);
+
+const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return &sdev->fw_ready.version;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
+
+/* module refcount management of SOF core */
+int sof_client_core_module_get(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ if (!try_module_get(sdev->dev->driver->owner))
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT);
+
+void sof_client_core_module_put(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ module_put(sdev->dev->driver->owner);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT);
+
+/* IPC event handling */
+void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
+{
+ struct sof_ipc_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
+ struct sof_ipc_event_entry *event;
+
+ mutex_lock(&sdev->client_event_handler_mutex);
+
+ list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
+ if (event->ipc_msg_type == msg_type)
+ event->callback(event->cdev, msg_buf);
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+
+int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
+ u32 ipc_msg_type,
+ sof_client_event_callback callback)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct sof_ipc_event_entry *event;
+
+ if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK))
+ return -EINVAL;
+
+ event = kmalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->ipc_msg_type = ipc_msg_type;
+ event->cdev = cdev;
+ event->callback = callback;
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->client_event_handler_mutex);
+ list_add(&event->list, &sdev->ipc_rx_handler_list);
+ mutex_unlock(&sdev->client_event_handler_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, SND_SOC_SOF_CLIENT);
+
+void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
+ u32 ipc_msg_type)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct sof_ipc_event_entry *event;
+
+ mutex_lock(&sdev->client_event_handler_mutex);
+
+ list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) {
+ if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) {
+ list_del(&event->list);
+ kfree(event);
+ break;
+ }
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT);
+
+/*DSP state notification and query */
+void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
+{
+ struct sof_state_event_entry *event;
+
+ mutex_lock(&sdev->client_event_handler_mutex);
+
+ list_for_each_entry(event, &sdev->fw_state_handler_list, list)
+ event->callback(event->cdev, sdev->fw_state);
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+
+int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
+ sof_client_fw_state_callback callback)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct sof_state_event_entry *event;
+
+ if (!callback)
+ return -EINVAL;
+
+ event = kmalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->cdev = cdev;
+ event->callback = callback;
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->client_event_handler_mutex);
+ list_add(&event->list, &sdev->fw_state_handler_list);
+ mutex_unlock(&sdev->client_event_handler_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT);
+
+void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct sof_state_event_entry *event;
+
+ mutex_lock(&sdev->client_event_handler_mutex);
+
+ list_for_each_entry(event, &sdev->fw_state_handler_list, list) {
+ if (event->cdev == cdev) {
+ list_del(&event->list);
+ kfree(event);
+ break;
+ }
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT);
+
+enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->fw_state;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT);
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
new file mode 100644
index 000000000000..4b6394b4c694
--- /dev/null
+++ b/sound/soc/sof/sof-client.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOC_SOF_CLIENT_H
+#define __SOC_SOF_CLIENT_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <sound/sof.h>
+
+struct sof_ipc_fw_version;
+struct sof_ipc_cmd_hdr;
+struct snd_sof_dev;
+struct dentry;
+
+/**
+ * struct sof_client_dev - SOF client device
+ * @auxdev: auxiliary device
+ * @sdev: pointer to SOF core device struct
+ * @list: item in SOF core client dev list
+ * @data: device specific data
+ */
+struct sof_client_dev {
+ struct auxiliary_device auxdev;
+ struct snd_sof_dev *sdev;
+ struct list_head list;
+ void *data;
+};
+
+#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev)
+
+#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \
+ container_of(auxiliary_dev, struct sof_client_dev, auxdev)
+
+#define dev_to_sof_client_dev(dev) \
+ container_of(to_auxiliary_dev(dev), struct sof_client_dev, auxdev)
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
+ void *reply_data, size_t reply_bytes);
+
+struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
+struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
+const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev);
+
+/* module refcount management of SOF core */
+int sof_client_core_module_get(struct sof_client_dev *cdev);
+void sof_client_core_module_put(struct sof_client_dev *cdev);
+
+/* IPC notification */
+typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *msg_buf);
+
+int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev,
+ u32 ipc_msg_type,
+ sof_client_event_callback callback);
+void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev,
+ u32 ipc_msg_type);
+
+/* DSP state notification and query */
+typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev,
+ enum sof_fw_state state);
+
+int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
+ sof_client_fw_state_callback callback);
+void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev);
+enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev);
+
+#endif /* __SOC_SOF_CLIENT_H */
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index 20c6ca37dbc4..4c9596742844 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -67,6 +67,22 @@ static const struct dmi_system_id sof_tplg_table[] = {
},
.driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg",
},
+ {
+ .callback = sof_tplg_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO_AMP-MAX98360_ALC5682VS_I2S_2WAY"),
+ },
+ .driver_data = "sof-adl-max98360a-rt5682-2way.tplg",
+ },
+ {
+ .callback = sof_tplg_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"),
+ DMI_MATCH(DMI_OEM_STRING, "AUDIO-AUDIO_MAX98357_ALC5682I_I2S_2WAY"),
+ },
+ .driver_data = "sof-adl-max98357a-rt5682-2way.tplg",
+ },
{}
};
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 087935192ce8..0d9b640ae24c 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -34,6 +34,9 @@
* on primary core
*/
#define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */
+#define SOF_DBG_IGNORE_D3_PERSISTENT BIT(7) /* ignore the DSP D3 persistent capability
+ * and always download firmware upon D3 exit
+ */
/* Flag definitions used for controlling the DSP dump behavior */
#define SOF_DBG_DUMP_REGS BIT(0)
@@ -66,26 +69,12 @@ bool sof_debug_check_flag(int mask);
#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT)
-#define ENABLE_DEBUGFS_CACHEBUF \
- (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \
- IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST))
-
/* So far the primary core on all DSPs has ID 0 */
#define SOF_DSP_PRIMARY_CORE 0
/* max number of DSP cores */
#define SOF_MAX_DSP_NUM_CORES 8
-/* DSP power state */
-enum sof_dsp_power_states {
- SOF_DSP_PM_D0,
- SOF_DSP_PM_D1,
- SOF_DSP_PM_D2,
- SOF_DSP_PM_D3_HOT,
- SOF_DSP_PM_D3,
- SOF_DSP_PM_D3_COLD,
-};
-
struct sof_dsp_power_state {
u32 state;
u32 substate; /* platform-specific */
@@ -116,6 +105,21 @@ struct snd_soc_tplg_ops;
struct snd_soc_component;
struct snd_sof_pdata;
+/**
+ * struct snd_sof_platform_stream_params - platform dependent stream parameters
+ * @stream_tag: Stream tag to use
+ * @use_phy_addr: Use the provided @phy_addr for configuration
+ * @phy_addr: Platform dependent address to be used, if @use_phy_addr
+ * is true
+ * @no_ipc_position: Disable position update IPC from firmware
+ */
+struct snd_sof_platform_stream_params {
+ u16 stream_tag;
+ bool use_phy_address;
+ u32 phy_addr;
+ bool no_ipc_position;
+};
+
/*
* SOF DSP HW abstraction operations.
* Used to abstract DSP HW architecture and any IO busses between host CPU
@@ -194,7 +198,7 @@ struct snd_sof_dsp_ops {
int (*pcm_hw_params)(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
- struct sof_ipc_stream_params *ipc_params); /* optional */
+ struct snd_sof_platform_stream_params *platform_params); /* optional */
/* host stream hw_free */
int (*pcm_hw_free)(struct snd_sof_dev *sdev,
@@ -212,36 +216,15 @@ struct snd_sof_dsp_ops {
/* pcm ack */
int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- /* Except for probe_pointer, all probe ops are mandatory */
- int (*probe_assign)(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai); /* mandatory */
- int (*probe_free)(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai); /* mandatory */
- int (*probe_set_params)(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai); /* mandatory */
- int (*probe_trigger)(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai); /* mandatory */
- int (*probe_pointer)(struct snd_sof_dev *sdev,
- struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai); /* optional */
-#endif
-
/* host read DSP stream data */
int (*ipc_msg_data)(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
void *p, size_t sz); /* mandatory */
- /* host configure DSP HW parameters */
- int (*ipc_pcm_params)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply); /* 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,
+ size_t posn_offset); /* optional */
/* pre/post firmware run */
int (*pre_fw_run)(struct snd_sof_dev *sof_dev); /* optional */
@@ -278,7 +261,7 @@ struct snd_sof_dsp_ops {
/* host DMA trace initialization */
int (*trace_init)(struct snd_sof_dev *sdev,
- u32 *stream_tag); /* optional */
+ struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */
int (*trace_release)(struct snd_sof_dev *sdev); /* optional */
int (*trace_trigger)(struct snd_sof_dev *sdev,
int cmd); /* optional */
@@ -299,6 +282,10 @@ struct snd_sof_dsp_ops {
void (*set_mach_params)(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev); /* optional */
+ /* IPC client ops */
+ int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
+ void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
+
/* DAI ops */
struct snd_soc_dai_driver *drv;
int num_drv;
@@ -329,13 +316,9 @@ struct snd_sof_dfsentry {
* or if it is accessible only when the DSP is in D0.
*/
enum sof_debugfs_access_type access_type;
-#if ENABLE_DEBUGFS_CACHEBUF
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
char *cache_buf; /* buffer to cache the contents of debugfs memory */
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
- void *msg_inject_tx;
- void *msg_inject_rx;
-#endif
struct snd_sof_dev *sdev;
struct list_head list; /* list in sdev dfsentry list */
union {
@@ -377,6 +360,46 @@ struct snd_sof_ipc_msg {
bool ipc_complete;
};
+/**
+ * struct sof_ipc_pm_ops - IPC-specific PM ops
+ * @ctx_save: Function pointer for context save
+ * @ctx_restore: Function pointer for context restore
+ */
+struct sof_ipc_pm_ops {
+ int (*ctx_save)(struct snd_sof_dev *sdev);
+ int (*ctx_restore)(struct snd_sof_dev *sdev);
+};
+
+struct sof_ipc_tplg_ops;
+struct sof_ipc_pcm_ops;
+
+/**
+ * struct sof_ipc_ops - IPC-specific ops
+ * @tplg: Pointer to IPC-specific topology ops
+ * @pm: Pointer to PM ops
+ * @pcm: Pointer to PCM ops
+ */
+struct sof_ipc_ops {
+ const struct sof_ipc_tplg_ops *tplg;
+ const struct sof_ipc_pm_ops *pm;
+ const struct sof_ipc_pcm_ops *pcm;
+};
+
+/* SOF generic IPC data */
+struct snd_sof_ipc {
+ struct snd_sof_dev *sdev;
+
+ /* protects messages and the disable flag */
+ struct mutex tx_mutex;
+ /* disables further sending of ipc's */
+ bool disable_ipc_tx;
+
+ struct snd_sof_ipc_msg msg;
+
+ /* IPC ops based on version */
+ const struct sof_ipc_ops *ops;
+};
+
/*
* SOF Device Level.
*/
@@ -446,6 +469,7 @@ struct snd_sof_dev {
struct list_head kcontrol_list;
struct list_head widget_list;
struct list_head dai_list;
+ struct list_head dai_link_list;
struct list_head route_list;
struct snd_soc_component *component;
u32 enabled_cores_mask; /* keep track of enabled cores */
@@ -457,10 +481,6 @@ struct snd_sof_dev {
int ipc_timeout;
int boot_timeout;
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
- unsigned int extractor_stream_tag;
-#endif
-
/* DMA for Trace */
struct snd_dma_buffer dmatb;
struct snd_dma_buffer dmatp;
@@ -486,6 +506,30 @@ struct snd_sof_dev {
*/
int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES];
+ /*
+ * Used to keep track of registered IPC client devices so that they can
+ * be removed when the parent SOF module is removed.
+ */
+ struct list_head ipc_client_list;
+
+ /* mutex to protect client list */
+ struct mutex ipc_client_mutex;
+
+ /*
+ * Used for tracking the IPC client's RX registration for DSP initiated
+ * message handling.
+ */
+ struct list_head ipc_rx_handler_list;
+
+ /*
+ * Used for tracking the IPC client's registration for DSP state change
+ * notification
+ */
+ struct list_head fw_state_handler_list;
+
+ /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */
+ struct mutex client_event_handler_mutex;
+
void *private; /* core does not touch this */
};
@@ -509,9 +553,10 @@ void snd_sof_complete(struct device *dev);
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
-int snd_sof_create_page_table(struct device *dev,
- struct snd_dma_buffer *dmab,
- unsigned char *page_table, size_t size);
+/*
+ * Compress support
+ */
+extern struct snd_compress_ops sof_compressed_ops;
/*
* Firmware loading.
@@ -531,8 +576,6 @@ void snd_sof_ipc_free(struct snd_sof_dev *sdev);
void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev);
void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev);
-int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev,
- struct sof_ipc_pcm_params *params);
int snd_sof_ipc_valid(struct snd_sof_dev *sdev);
int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
void *msg_data, size_t msg_bytes, void *reply_data,
@@ -593,15 +636,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops;
/*
* Firmware state tracking
*/
-static inline void sof_set_fw_state(struct snd_sof_dev *sdev,
- enum sof_fw_state new_state)
-{
- if (sdev->fw_state == new_state)
- return;
-
- dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
- sdev->fw_state = new_state;
-}
+void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state);
/*
* Utilities
@@ -624,9 +659,9 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id);
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream,
void *p, size_t sz);
-int sof_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply);
+int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ size_t posn_offset);
int sof_stream_pcm_open(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
@@ -634,4 +669,56 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int sof_machine_check(struct snd_sof_dev *sdev);
+
+/* SOF client support */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
+ const void *data, size_t size);
+void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id);
+int sof_register_clients(struct snd_sof_dev *sdev);
+void sof_unregister_clients(struct snd_sof_dev *sdev);
+void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf);
+void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev);
+int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state);
+int sof_resume_clients(struct snd_sof_dev *sdev);
+#else /* CONFIG_SND_SOC_SOF_CLIENT */
+static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name,
+ u32 id, const void *data, size_t size)
+{
+ return 0;
+}
+
+static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev,
+ const char *name, u32 id)
+{
+}
+
+static inline int sof_register_clients(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void sof_unregister_clients(struct snd_sof_dev *sdev)
+{
+}
+
+static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
+{
+}
+
+static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
+{
+}
+
+static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state)
+{
+ return 0;
+}
+
+static inline int sof_resume_clients(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+#endif /* CONFIG_SND_SOC_SOF_CLIENT */
+
#endif
diff --git a/sound/soc/sof/sof-probes.c b/sound/soc/sof/sof-probes.c
deleted file mode 100644
index c79026cdb8c7..000000000000
--- a/sound/soc/sof/sof-probes.c
+++ /dev/null
@@ -1,364 +0,0 @@
-// 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) 2019-2021 Intel Corporation. All rights reserved.
-// Author: Cezary Rojewski <cezary.rojewski@intel.com>
-//
-
-#include <sound/soc.h>
-#include "ops.h"
-#include "sof-priv.h"
-#include "sof-probes.h"
-
-struct sof_probe_dma {
- unsigned int stream_tag;
- unsigned int dma_buffer_size;
-} __packed;
-
-struct sof_ipc_probe_dma_add_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- struct sof_probe_dma dma[];
-} __packed;
-
-struct sof_ipc_probe_info_params {
- struct sof_ipc_reply rhdr;
- unsigned int num_elems;
- union {
- struct sof_probe_dma dma[0];
- struct sof_probe_point_desc desc[0];
- };
-} __packed;
-
-struct sof_ipc_probe_point_add_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- struct sof_probe_point_desc desc[];
-} __packed;
-
-struct sof_ipc_probe_point_remove_params {
- struct sof_ipc_cmd_hdr hdr;
- unsigned int num_elems;
- unsigned int buffer_id[];
-} __packed;
-
-/**
- * sof_ipc_probe_init - initialize data probing
- * @sdev: SOF sound device
- * @stream_tag: Extractor stream tag
- * @buffer_size: DMA buffer size to set for extractor
- *
- * Host chooses whether extraction is supported or not by providing
- * valid stream tag to DSP. Once specified, stream described by that
- * tag will be tied to DSP for extraction for the entire lifetime of
- * probe.
- *
- * Probing is initialized only once and each INIT request must be
- * matched by DEINIT call.
- */
-static int sof_ipc_probe_init(struct snd_sof_dev *sdev, u32 stream_tag,
- size_t buffer_size)
-{
- struct sof_ipc_probe_dma_add_params *msg;
- struct sof_ipc_reply reply;
- size_t size = struct_size(msg, dma, 1);
- int ret;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- msg->hdr.size = size;
- msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT;
- msg->num_elems = 1;
- msg->dma[0].stream_tag = stream_tag;
- msg->dma[0].dma_buffer_size = buffer_size;
-
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- kfree(msg);
- return ret;
-}
-
-/**
- * sof_ipc_probe_deinit - cleanup after data probing
- * @sdev: SOF sound device
- *
- * Host sends DEINIT request to free previously initialized probe
- * on DSP side once it is no longer needed. DEINIT only when there
- * are no probes connected and with all injectors detached.
- */
-static int sof_ipc_probe_deinit(struct snd_sof_dev *sdev)
-{
- struct sof_ipc_cmd_hdr msg;
- struct sof_ipc_reply reply;
-
- msg.size = sizeof(msg);
- msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
-
- return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size,
- &reply, sizeof(reply));
-}
-
-static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd,
- void **params, size_t *num_params)
-{
- struct sof_ipc_probe_info_params msg = {{{0}}};
- struct sof_ipc_probe_info_params *reply;
- size_t bytes;
- int ret;
-
- *params = NULL;
- *num_params = 0;
-
- reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
- if (!reply)
- return -ENOMEM;
- msg.rhdr.hdr.size = sizeof(msg);
- msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
-
- ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg,
- msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE);
- if (ret < 0 || reply->rhdr.error < 0)
- goto exit;
-
- if (!reply->num_elems)
- goto exit;
-
- if (cmd == SOF_IPC_PROBE_DMA_INFO)
- bytes = sizeof(reply->dma[0]);
- else
- bytes = sizeof(reply->desc[0]);
- bytes *= reply->num_elems;
- *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
- if (!*params) {
- ret = -ENOMEM;
- goto exit;
- }
- *num_params = reply->num_elems;
-
-exit:
- kfree(reply);
- return ret;
-}
-
-/**
- * sof_ipc_probe_points_info - retrieve list of active probe points
- * @sdev: SOF sound device
- * @desc: Returned list of active probes
- * @num_desc: Returned count of active probes
- *
- * Host sends PROBE_POINT_INFO request to obtain list of active probe
- * points, valid for disconnection when given probe is no longer
- * required.
- */
-int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc **desc,
- size_t *num_desc)
-{
- return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO,
- (void **)desc, num_desc);
-}
-EXPORT_SYMBOL(sof_ipc_probe_points_info);
-
-/**
- * sof_ipc_probe_points_add - connect specified probes
- * @sdev: SOF sound device
- * @desc: List of probe points to connect
- * @num_desc: Number of elements in @desc
- *
- * Dynamically connects to provided set of endpoints. Immediately
- * after connection is established, host must be prepared to
- * transfer data from or to target stream given the probing purpose.
- *
- * Each probe point should be removed using PROBE_POINT_REMOVE
- * request when no longer needed.
- */
-int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc *desc, size_t num_desc)
-{
- struct sof_ipc_probe_point_add_params *msg;
- struct sof_ipc_reply reply;
- size_t size = struct_size(msg, desc, num_desc);
- int ret;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- msg->hdr.size = size;
- msg->num_elems = num_desc;
- msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
- memcpy(&msg->desc[0], desc, size - sizeof(*msg));
-
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- kfree(msg);
- return ret;
-}
-EXPORT_SYMBOL(sof_ipc_probe_points_add);
-
-/**
- * sof_ipc_probe_points_remove - disconnect specified probes
- * @sdev: SOF sound device
- * @buffer_id: List of probe points to disconnect
- * @num_buffer_id: Number of elements in @desc
- *
- * Removes previously connected probes from list of active probe
- * points and frees all resources on DSP side.
- */
-int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
- unsigned int *buffer_id, size_t num_buffer_id)
-{
- struct sof_ipc_probe_point_remove_params *msg;
- struct sof_ipc_reply reply;
- size_t size = struct_size(msg, buffer_id, num_buffer_id);
- int ret;
-
- msg = kmalloc(size, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- msg->hdr.size = size;
- msg->num_elems = num_buffer_id;
- msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
- memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
-
- ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size,
- &reply, sizeof(reply));
- kfree(msg);
- return ret;
-}
-EXPORT_SYMBOL(sof_ipc_probe_points_remove);
-
-static int sof_probe_compr_startup(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- int ret;
-
- ret = snd_sof_probe_compr_assign(sdev, cstream, dai);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
- return ret;
- }
-
- sdev->extractor_stream_tag = ret;
- return 0;
-}
-
-static int sof_probe_compr_shutdown(struct snd_compr_stream *cstream,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct sof_probe_point_desc *desc;
- size_t num_desc;
- int i, ret;
-
- /* disconnect all probe points */
- ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
- goto exit;
- }
-
- for (i = 0; i < num_desc; i++)
- sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1);
- kfree(desc);
-
-exit:
- ret = sof_ipc_probe_deinit(sdev);
- if (ret < 0)
- dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
-
- sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
- snd_compr_free_pages(cstream);
-
- return snd_sof_probe_compr_free(sdev, cstream, dai);
-}
-
-static int sof_probe_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct snd_compr_runtime *rtd = cstream->runtime;
- int ret;
-
- cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
- cstream->dma_buffer.dev.dev = sdev->dev;
- ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
- if (ret < 0)
- return ret;
-
- ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai);
- if (ret < 0)
- return ret;
-
- ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag,
- rtd->dma_bytes);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to init probe: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
-
- return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai);
-}
-
-static int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp,
- struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
-
- return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai);
-}
-
-const struct snd_soc_cdai_ops sof_probe_compr_ops = {
- .startup = sof_probe_compr_startup,
- .shutdown = sof_probe_compr_shutdown,
- .set_params = sof_probe_compr_set_params,
- .trigger = sof_probe_compr_trigger,
- .pointer = sof_probe_compr_pointer,
-};
-EXPORT_SYMBOL(sof_probe_compr_ops);
-
-static int sof_probe_compr_copy(struct snd_soc_component *component,
- struct snd_compr_stream *cstream,
- char __user *buf, size_t count)
-{
- struct snd_compr_runtime *rtd = cstream->runtime;
- unsigned int offset, n;
- void *ptr;
- int ret;
-
- if (count > rtd->buffer_size)
- count = rtd->buffer_size;
-
- div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
- ptr = rtd->dma_area + offset;
- n = rtd->buffer_size - offset;
-
- if (count < n) {
- ret = copy_to_user(buf, ptr, count);
- } else {
- ret = copy_to_user(buf, ptr, n);
- ret += copy_to_user(buf + n, rtd->dma_area, count - n);
- }
-
- if (ret)
- return count - ret;
- return count;
-}
-
-const struct snd_compress_ops sof_probe_compressed_ops = {
- .copy = sof_probe_compr_copy,
-};
-EXPORT_SYMBOL(sof_probe_compressed_ops);
diff --git a/sound/soc/sof/sof-probes.h b/sound/soc/sof/sof-probes.h
deleted file mode 100644
index 4a1ed2942d28..000000000000
--- a/sound/soc/sof/sof-probes.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* 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) 2019-2021 Intel Corporation. All rights reserved.
- * Author: Cezary Rojewski <cezary.rojewski@intel.com>
- */
-
-#ifndef __SOF_PROBES_H
-#define __SOF_PROBES_H
-
-#include <sound/compress_driver.h>
-#include <sound/sof/header.h>
-
-struct snd_sof_dev;
-
-#define SOF_PROBE_INVALID_NODE_ID UINT_MAX
-
-struct sof_probe_point_desc {
- unsigned int buffer_id;
- unsigned int purpose;
- unsigned int stream_tag;
-} __packed;
-
-int sof_ipc_probe_points_info(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc **desc,
- size_t *num_desc);
-int sof_ipc_probe_points_add(struct snd_sof_dev *sdev,
- struct sof_probe_point_desc *desc,
- size_t num_desc);
-int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev,
- unsigned int *buffer_id, size_t num_buffer_id);
-
-extern const struct snd_soc_cdai_ops sof_probe_compr_ops;
-extern const struct snd_compress_ops sof_probe_compressed_ops;
-
-#endif
diff --git a/sound/soc/sof/sof-utils.c b/sound/soc/sof/sof-utils.c
new file mode 100644
index 000000000000..a3300ecee062
--- /dev/null
+++ b/sound/soc/sof/sof-utils.c
@@ -0,0 +1,77 @@
+// 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) 2018-2022 Intel Corporation. All rights reserved.
+//
+// Author: Keyon Jie <yang.jie@linux.intel.com>
+//
+
+#include <asm/unaligned.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/device.h>
+#include <sound/memalloc.h>
+#include <linux/module.h>
+#include "sof-utils.h"
+
+/*
+ * Generic buffer page table creation.
+ * Take the each physical page address and drop the least significant unused
+ * bits from each (based on PAGE_SIZE). Then pack valid page address bits
+ * into compressed page table.
+ */
+
+int snd_sof_create_page_table(struct device *dev,
+ struct snd_dma_buffer *dmab,
+ unsigned char *page_table, size_t size)
+{
+ int i, pages;
+
+ pages = snd_sgbuf_aligned_pages(size);
+
+ dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
+ dmab->area, size, pages);
+
+ for (i = 0; i < pages; i++) {
+ /*
+ * The number of valid address bits for each page is 20.
+ * idx determines the byte position within page_table
+ * where the current page's address is stored
+ * in the compressed page_table.
+ * This can be calculated by multiplying the page number by 2.5.
+ */
+ u32 idx = (5 * i) >> 1;
+ u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
+ u8 *pg_table;
+
+ dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
+
+ pg_table = (u8 *)(page_table + idx);
+
+ /*
+ * pagetable compression:
+ * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
+ * ___________pfn 0__________ __________pfn 1___________ _pfn 2...
+ * .... .... .... .... .... .... .... .... .... .... ....
+ * It is created by:
+ * 1. set current location to 0, PFN index i to 0
+ * 2. put pfn[i] at current location in Little Endian byte order
+ * 3. calculate an intermediate value as
+ * x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
+ * 4. put x at offset (current location + 2) in LE byte order
+ * 5. increment current location by 5 bytes, increment i by 2
+ * 6. continue to (2)
+ */
+ if (i & 1)
+ put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
+ pg_table);
+ else
+ put_unaligned_le32(pfn, pg_table);
+ }
+
+ return pages;
+}
+EXPORT_SYMBOL(snd_sof_create_page_table);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/sof-utils.h b/sound/soc/sof/sof-utils.h
new file mode 100644
index 000000000000..6f902893807e
--- /dev/null
+++ b/sound/soc/sof/sof-utils.h
@@ -0,0 +1,19 @@
+/* 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 __SOC_SOF_UTILS_H
+#define __SOC_SOF_UTILS_H
+
+struct snd_dma_buffer;
+struct device;
+
+int snd_sof_create_page_table(struct device *dev,
+ struct snd_dma_buffer *dmab,
+ unsigned char *page_table, size_t size);
+
+#endif
diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c
index 15a55851faeb..5f1ceeea893a 100644
--- a/sound/soc/sof/stream-ipc.c
+++ b/sound/soc/sof/stream-ipc.c
@@ -45,12 +45,11 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev,
}
EXPORT_SYMBOL(sof_ipc_msg_data);
-int sof_ipc_pcm_params(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
- const struct sof_ipc_pcm_params_reply *reply)
+int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ size_t posn_offset)
{
struct sof_stream *stream = substream->runtime->private_data;
- size_t posn_offset = reply->posn_offset;
/* check if offset is overflow or it is not aligned */
if (posn_offset > sdev->stream_box.size ||
@@ -64,7 +63,7 @@ int sof_ipc_pcm_params(struct snd_sof_dev *sdev,
return 0;
}
-EXPORT_SYMBOL(sof_ipc_pcm_params);
+EXPORT_SYMBOL(sof_set_stream_data_offset);
int sof_stream_pcm_open(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream)
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index e72dcae5e7ee..9b11e9795a7a 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -14,7 +14,6 @@
#include <linux/firmware.h>
#include <linux/workqueue.h>
#include <sound/tlv.h>
-#include <sound/pcm_params.h>
#include <uapi/sound/sof/tokens.h>
#include "sof-priv.h"
#include "sof-audio.h"
@@ -28,15 +27,9 @@
#define VOL_TWENTIETH_ROOT_OF_TEN 73533
/* 40th root of 10 in Q1.16 fixed-point notation*/
#define VOL_FORTIETH_ROOT_OF_TEN 69419
-/*
- * Volume fractional word length define to 16 sets
- * the volume linear gain value to use Qx.16 format
- */
-#define VOLUME_FWL 16
+
/* 0.5 dB step value in topology TLV */
#define VOL_HALF_DB_STEP 50
-/* Full volume for default values */
-#define VOL_ZERO_DB BIT(VOLUME_FWL)
/* TLV data items */
#define TLV_ITEMS 3
@@ -47,178 +40,100 @@
/* size of tplg abi in byte */
#define SOF_TPLG_ABI_SIZE 3
-struct sof_widget_data {
- int ctrl_type;
- int ipc_cmd;
- struct sof_abi_hdr *pdata;
- struct snd_sof_control *control;
-};
-
-/* send pcm params ipc */
-static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir)
+/**
+ * sof_update_ipc_object - Parse multiple sets of tokens within the token array associated with the
+ * token ID.
+ * @scomp: pointer to SOC component
+ * @object: target IPC struct to save the parsed values
+ * @token_id: token ID for the token array to be searched
+ * @tuples: pointer to the tuples array
+ * @num_tuples: number of tuples in the tuples array
+ * @object_size: size of the object
+ * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function
+ * looks for @token_instance_num of each token in the token array associated
+ * with the @token_id
+ */
+int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
+ struct snd_sof_tuple *tuples, int num_tuples,
+ size_t object_size, int token_instance_num)
{
- struct sof_ipc_pcm_params_reply ipc_params_reply;
- struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_pcm_params pcm;
- struct snd_pcm_hw_params *params;
- struct snd_sof_pcm *spcm;
- int ret;
-
- memset(&pcm, 0, sizeof(pcm));
+ 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_topology_token *tokens;
+ int i, j;
- /* get runtime PCM params using widget's stream name */
- spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
- if (!spcm) {
- dev_err(scomp->dev, "error: cannot find PCM for %s\n",
- swidget->widget->name);
+ if (token_list[token_id].count < 0) {
+ dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id);
return -EINVAL;
}
- params = &spcm->params[dir];
-
- /* set IPC PCM params */
- pcm.hdr.size = sizeof(pcm);
- pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
- pcm.comp_id = swidget->comp_id;
- pcm.params.hdr.size = sizeof(pcm.params);
- pcm.params.direction = dir;
- pcm.params.sample_valid_bytes = params_width(params) >> 3;
- pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
- pcm.params.rate = params_rate(params);
- pcm.params.channels = params_channels(params);
- pcm.params.host_period_bytes = params_period_bytes(params);
-
- /* set format */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
- break;
- case SNDRV_PCM_FORMAT_S24:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
- break;
- case SNDRV_PCM_FORMAT_S32:
- pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
- break;
- default:
+ /* No tokens to match */
+ if (!token_list[token_id].count)
+ return 0;
+
+ tokens = token_list[token_id].tokens;
+ if (!tokens) {
+ dev_err(scomp->dev, "Invalid tokens for token id: %d\n", token_id);
return -EINVAL;
}
- /* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
- &ipc_params_reply, sizeof(ipc_params_reply));
- if (ret < 0)
- dev_err(scomp->dev, "error: pcm params failed for %s\n",
- swidget->widget->name);
-
- return ret;
-}
-
- /* send stream trigger ipc */
-static int ipc_trigger(struct snd_sof_widget *swidget, int cmd)
-{
- struct snd_soc_component *scomp = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
- int ret;
-
- /* set IPC stream params */
- stream.hdr.size = sizeof(stream);
- stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd;
- stream.comp_id = swidget->comp_id;
-
- /* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
- sizeof(stream), &reply, sizeof(reply));
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to trigger %s\n",
- swidget->widget->name);
-
- return ret;
-}
-
-static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *scomp;
- int stream = SNDRV_PCM_STREAM_CAPTURE;
- struct snd_sof_pcm *spcm;
- int ret = 0;
-
- if (!swidget)
- return 0;
+ for (i = 0; i < token_list[token_id].count; i++) {
+ int offset = 0;
+ int num_tokens_matched = 0;
- scomp = swidget->scomp;
+ for (j = 0; j < num_tuples; j++) {
+ if (tokens[i].token == tuples[j].token) {
+ switch (tokens[i].type) {
+ case SND_SOC_TPLG_TUPLE_TYPE_WORD:
+ {
+ u32 *val = (u32 *)((u8 *)object + tokens[i].offset +
+ offset);
- dev_dbg(scomp->dev, "received event %d for widget %s\n",
- event, w->name);
+ *val = tuples[j].value.v;
+ break;
+ }
+ case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
+ case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
+ {
+ u16 *val = (u16 *)((u8 *)object + tokens[i].offset +
+ offset);
- /* get runtime PCM params using widget's stream name */
- spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
- if (!spcm) {
- dev_err(scomp->dev, "error: cannot find PCM for %s\n",
- swidget->widget->name);
- return -EINVAL;
- }
+ *val = (u16)tuples[j].value.v;
+ break;
+ }
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+ {
+ if (!tokens[i].get_token) {
+ dev_err(scomp->dev,
+ "get_token not defined for token %d in %s\n",
+ tokens[i].token, token_list[token_id].name);
+ return -EINVAL;
+ }
+
+ tokens[i].get_token((void *)tuples[j].value.s, object,
+ tokens[i].offset + offset);
+ break;
+ }
+ default:
+ break;
+ }
- /* process events */
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (spcm->stream[stream].suspend_ignored) {
- dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
- return 0;
- }
+ num_tokens_matched++;
- /* set pcm params */
- ret = ipc_pcm_params(swidget, stream);
- if (ret < 0) {
- dev_err(scomp->dev,
- "error: failed to set pcm params for widget %s\n",
- swidget->widget->name);
- break;
- }
+ /* found all required sets of current token. Move to the next one */
+ if (!(num_tokens_matched % token_instance_num))
+ break;
- /* start trigger */
- ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START);
- if (ret < 0)
- dev_err(scomp->dev,
- "error: failed to trigger widget %s\n",
- swidget->widget->name);
- break;
- case SND_SOC_DAPM_POST_PMD:
- if (spcm->stream[stream].suspend_ignored) {
- dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n");
- return 0;
+ /* move to the next object */
+ offset += object_size;
+ }
}
-
- /* stop trigger */
- ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
- if (ret < 0)
- dev_err(scomp->dev,
- "error: failed to trigger widget %s\n",
- swidget->widget->name);
-
- /* pcm free */
- ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE);
- if (ret < 0)
- dev_err(scomp->dev,
- "error: failed to trigger widget %s\n",
- swidget->widget->name);
- break;
- default:
- break;
}
- return ret;
+ return 0;
}
-/* event handlers for keyword detect component */
-static const struct snd_soc_tplg_widget_events sof_kwd_events[] = {
- {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_keyword_dapm_event},
-};
-
static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS])
{
/* we only support dB scale TLV type at the moment */
@@ -423,62 +338,7 @@ static enum sof_ipc_frame find_format(const char *name)
return SOF_IPC_FRAME_S32_LE;
}
-struct sof_process_types {
- const char *name;
- enum sof_ipc_process_type type;
- enum sof_comp_type comp_type;
-};
-
-static const struct sof_process_types sof_process[] = {
- {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR},
- {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR},
- {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT},
- {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB},
- {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR},
- {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX},
- {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX},
- {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK},
- {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP},
-};
-
-static enum sof_ipc_process_type find_process(const char *name)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(sof_process); i++) {
- if (strcmp(name, sof_process[i].name) == 0)
- return sof_process[i].type;
- }
-
- return SOF_PROCESS_NONE;
-}
-
-static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(sof_process); i++) {
- if (sof_process[i].type == type)
- return sof_process[i].comp_type;
- }
-
- return SOF_COMP_NONE;
-}
-
-/*
- * Topology Token Parsing.
- * New tokens should be added to headers and parsing tables below.
- */
-
-struct sof_topology_token {
- u32 token;
- u32 type;
- int (*get_token)(void *elem, void *object, u32 offset, u32 size);
- u32 offset;
- u32 size;
-};
-
-static int get_token_u32(void *elem, void *object, u32 offset, u32 size)
+int get_token_u32(void *elem, void *object, u32 offset)
{
struct snd_soc_tplg_vendor_value_elem *velem = elem;
u32 *val = (u32 *)((u8 *)object + offset);
@@ -487,7 +347,7 @@ static int get_token_u32(void *elem, void *object, u32 offset, u32 size)
return 0;
}
-static int get_token_u16(void *elem, void *object, u32 offset, u32 size)
+int get_token_u16(void *elem, void *object, u32 offset)
{
struct snd_soc_tplg_vendor_value_elem *velem = elem;
u16 *val = (u16 *)((u8 *)object + offset);
@@ -496,7 +356,7 @@ static int get_token_u16(void *elem, void *object, u32 offset, u32 size)
return 0;
}
-static int get_token_uuid(void *elem, void *object, u32 offset, u32 size)
+int get_token_uuid(void *elem, void *object, u32 offset)
{
struct snd_soc_tplg_vendor_uuid_elem *velem = elem;
u8 *dst = (u8 *)object + offset;
@@ -506,326 +366,53 @@ static int get_token_uuid(void *elem, void *object, u32 offset, u32 size)
return 0;
}
-static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size)
-{
- struct snd_soc_tplg_vendor_string_elem *velem = elem;
- u32 *val = (u32 *)((u8 *)object + offset);
-
- *val = find_format(velem->string);
- return 0;
-}
-
-static int get_token_dai_type(void *elem, void *object, u32 offset, u32 size)
+int get_token_comp_format(void *elem, void *object, u32 offset)
{
- struct snd_soc_tplg_vendor_string_elem *velem = elem;
u32 *val = (u32 *)((u8 *)object + offset);
- *val = find_dai(velem->string);
+ *val = find_format((const char *)elem);
return 0;
}
-static int get_token_process_type(void *elem, void *object, u32 offset,
- u32 size)
+int get_token_dai_type(void *elem, void *object, u32 offset)
{
- struct snd_soc_tplg_vendor_string_elem *velem = elem;
u32 *val = (u32 *)((u8 *)object + offset);
- *val = find_process(velem->string);
+ *val = find_dai((const char *)elem);
return 0;
}
-/* Buffers */
-static const struct sof_topology_token buffer_tokens[] = {
- {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_buffer, size), 0},
- {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_buffer, caps), 0},
-};
-
-/* DAI */
-static const struct sof_topology_token dai_tokens[] = {
- {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
- offsetof(struct sof_ipc_comp_dai, type), 0},
- {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_dai, dai_index), 0},
- {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_dai, direction), 0},
-};
-
-/* BE DAI link */
-static const struct sof_topology_token dai_link_tokens[] = {
- {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
- offsetof(struct sof_ipc_dai_config, type), 0},
- {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_config, dai_index), 0},
-};
-
-/* scheduling */
-static const struct sof_topology_token sched_tokens[] = {
- {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, period), 0},
- {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, priority), 0},
- {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, period_mips), 0},
- {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, core), 0},
- {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, frames_per_sched), 0},
- {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_pipe_new, time_domain), 0},
-};
-
-static const struct sof_topology_token pipeline_tokens[] = {
- {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
- offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0},
-
-};
-
-/* volume */
-static const struct sof_topology_token volume_tokens[] = {
- {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp), 0},
- {SOF_TKN_VOLUME_RAMP_STEP_MS,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_volume, initial_ramp), 0},
-};
-
-/* SRC */
-static const struct sof_topology_token src_tokens[] = {
- {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_src, source_rate), 0},
- {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_src, sink_rate), 0},
-};
-
-/* ASRC */
-static const struct sof_topology_token asrc_tokens[] = {
- {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_asrc, source_rate), 0},
- {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_asrc, sink_rate), 0},
- {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32,
- offsetof(struct sof_ipc_comp_asrc, asynchronous_mode), 0},
- {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32,
- offsetof(struct sof_ipc_comp_asrc, operation_mode), 0},
-};
-
-/* Tone */
-static const struct sof_topology_token tone_tokens[] = {
-};
-
-/* EFFECT */
-static const struct sof_topology_token process_tokens[] = {
- {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING,
- get_token_process_type,
- offsetof(struct sof_ipc_comp_process, type), 0},
-};
-
-/* PCM */
-static const struct sof_topology_token pcm_tokens[] = {
- {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_host, dmac_config), 0},
-};
-
/* PCM */
static const struct sof_topology_token stream_tokens[] = {
- {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3,
- SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
- offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible), 0},
- {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3,
- SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
- offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible), 0},
-};
-
-/* Generic components */
-static const struct sof_topology_token comp_tokens[] = {
- {SOF_TKN_COMP_PERIOD_SINK_COUNT,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_config, periods_sink), 0},
- {SOF_TKN_COMP_PERIOD_SOURCE_COUNT,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp_config, periods_source), 0},
- {SOF_TKN_COMP_FORMAT,
- SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
- offsetof(struct sof_ipc_comp_config, frame_fmt), 0},
-};
-
-/* SSP */
-static const struct sof_topology_token ssp_tokens[] = {
- {SOF_TKN_INTEL_SSP_CLKS_CONTROL,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_ssp_params, clks_control), 0},
- {SOF_TKN_INTEL_SSP_MCLK_ID,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_ssp_params, mclk_id), 0},
- {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32,
- offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits), 0},
- {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT,
- get_token_u16,
- offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width), 0},
- {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32,
- offsetof(struct sof_ipc_dai_ssp_params, quirks), 0},
- {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL,
- get_token_u16,
- offsetof(struct sof_ipc_dai_ssp_params,
- tdm_per_slot_padding_flag), 0},
- {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32,
- offsetof(struct sof_ipc_dai_ssp_params, bclk_delay), 0},
-
-};
-
-/* ALH */
-static const struct sof_topology_token alh_tokens[] = {
- {SOF_TKN_INTEL_ALH_RATE,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_alh_params, rate), 0},
- {SOF_TKN_INTEL_ALH_CH,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_alh_params, channels), 0},
-};
-
-/* DMIC */
-static const struct sof_topology_token dmic_tokens[] = {
- {SOF_TKN_INTEL_DMIC_DRIVER_VERSION,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version),
- 0},
- {SOF_TKN_INTEL_DMIC_CLK_MIN,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min), 0},
- {SOF_TKN_INTEL_DMIC_CLK_MAX,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max), 0},
- {SOF_TKN_INTEL_DMIC_SAMPLE_RATE,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params, fifo_fs), 0},
- {SOF_TKN_INTEL_DMIC_DUTY_MIN,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_params, duty_min), 0},
- {SOF_TKN_INTEL_DMIC_DUTY_MAX,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_params, duty_max), 0},
- {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params,
- num_pdm_active), 0},
- {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_params, fifo_bits), 0},
- {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time), 0},
-
-};
-
-/* ESAI */
-static const struct sof_topology_token esai_tokens[] = {
- {SOF_TKN_IMX_ESAI_MCLK_ID,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0},
-};
-
-/* SAI */
-static const struct sof_topology_token sai_tokens[] = {
- {SOF_TKN_IMX_SAI_MCLK_ID,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_sai_params, mclk_id), 0},
-};
-
-/* Core tokens */
-static const struct sof_topology_token core_tokens[] = {
- {SOF_TKN_COMP_CORE_ID,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_comp, core), 0},
-};
-
-/* Component extended tokens */
-static const struct sof_topology_token comp_ext_tokens[] = {
- {SOF_TKN_COMP_UUID,
- SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
- offsetof(struct sof_ipc_comp_ext, uuid), 0},
-};
-
-/*
- * DMIC PDM Tokens
- * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token
- * as it increments the index while parsing the array of pdm tokens
- * and determines the correct offset
- */
-static const struct sof_topology_token dmic_pdm_tokens[] = {
- {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge),
- 0},
- {SOF_TKN_INTEL_DMIC_PDM_SKEW,
- SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
- offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew),
- 0},
-};
-
-/* HDA */
-static const struct sof_topology_token hda_tokens[] = {
- {SOF_TKN_INTEL_HDA_RATE,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_hda_params, rate), 0},
- {SOF_TKN_INTEL_HDA_CH,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_hda_params, channels), 0},
+ {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)},
+ {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)},
};
/* Leds */
static const struct sof_topology_token led_tokens[] = {
{SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct snd_sof_led_control, use_led), 0},
- {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32, offsetof(struct snd_sof_led_control, direction), 0},
-};
-
-/* AFE */
-static const struct sof_topology_token afe_tokens[] = {
- {SOF_TKN_MEDIATEK_AFE_RATE,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0},
- {SOF_TKN_MEDIATEK_AFE_CH,
- SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0},
- {SOF_TKN_MEDIATEK_AFE_FORMAT,
- SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,
- offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0},
+ offsetof(struct snd_sof_led_control, use_led)},
+ {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_led_control, direction)},
};
+/**
+ * sof_parse_uuid_tokens - Parse multiple sets of UUID tokens
+ * @scomp: pointer to soc component
+ * @object: target ipc struct for parsed values
+ * @offset: offset within the object pointer
+ * @tokens: array of struct sof_topology_token containing the tokens to be matched
+ * @num_tokens: number of tokens in tokens array
+ * @array: source pointer to consecutive vendor arrays in topology
+ *
+ * This function parses multiple sets of string type tokens in vendor arrays
+ */
static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array,
- size_t offset)
+ void *object, size_t offset,
+ const struct sof_topology_token *tokens, int num_tokens,
+ struct snd_soc_tplg_vendor_array *array)
{
struct snd_soc_tplg_vendor_uuid_elem *elem;
int found = 0;
@@ -836,7 +423,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
elem = &array->uuid[i];
/* search for token */
- for (j = 0; j < count; j++) {
+ for (j = 0; j < num_tokens; j++) {
/* match token type */
if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID)
continue;
@@ -847,8 +434,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
/* matched - now load token */
tokens[j].get_token(elem, object,
- offset + tokens[j].offset,
- tokens[j].size);
+ offset + tokens[j].offset);
found++;
}
@@ -857,12 +443,138 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
return found;
}
+/**
+ * sof_copy_tuples - Parse tokens and copy them to the @tuples array
+ * @sdev: pointer to struct snd_sof_dev
+ * @array: source pointer to consecutive vendor arrays in topology
+ * @array_size: size of @array
+ * @token_id: Token ID associated with a token array
+ * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function
+ * looks for @token_instance_num of each token in the token array associated
+ * with the @token_id
+ * @tuples: tuples array to copy the matched tuples to
+ * @tuples_size: size of @tuples
+ * @num_copied_tuples: pointer to the number of copied tuples in the tuples array
+ *
+ */
+static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array,
+ 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_topology_token *tokens;
+ int found = 0;
+ int num_tokens, asize;
+ int i, j;
+
+ /* nothing to do if token_list is NULL */
+ if (!token_list)
+ return 0;
+
+ if (!tuples || !num_copied_tuples) {
+ dev_err(sdev->dev, "Invalid tuples array\n");
+ return -EINVAL;
+ }
+
+ tokens = token_list[token_id].tokens;
+ num_tokens = token_list[token_id].count;
+
+ if (!tokens) {
+ dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id);
+ return -EINVAL;
+ }
+
+ /* check if there's space in the tuples array for new tokens */
+ if (*num_copied_tuples >= tuples_size) {
+ dev_err(sdev->dev, "No space in tuples array for new tokens from %s",
+ token_list[token_id].name);
+ return -EINVAL;
+ }
+
+ while (array_size > 0 && found < num_tokens * token_instance_num) {
+ asize = le32_to_cpu(array->size);
+
+ /* validate asize */
+ if (asize < 0) {
+ dev_err(sdev->dev, "Invalid array size 0x%x\n", asize);
+ return -EINVAL;
+ }
+
+ /* make sure there is enough data before parsing */
+ array_size -= asize;
+ if (array_size < 0) {
+ dev_err(sdev->dev, "Invalid array size 0x%x\n", asize);
+ return -EINVAL;
+ }
+
+ /* parse element by element */
+ for (i = 0; i < le32_to_cpu(array->num_elems); i++) {
+ /* search for token */
+ for (j = 0; j < num_tokens; j++) {
+ /* match token type */
+ if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL ||
+ tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING))
+ continue;
+
+ if (tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING) {
+ struct snd_soc_tplg_vendor_string_elem *elem;
+
+ elem = &array->string[i];
+
+ /* match token id */
+ if (tokens[j].token != le32_to_cpu(elem->token))
+ continue;
+
+ tuples[*num_copied_tuples].token = tokens[j].token;
+ tuples[*num_copied_tuples].value.s = elem->string;
+ } else {
+ struct snd_soc_tplg_vendor_value_elem *elem;
+
+ elem = &array->value[i];
+
+ /* match token id */
+ if (tokens[j].token != le32_to_cpu(elem->token))
+ continue;
+
+ tuples[*num_copied_tuples].token = tokens[j].token;
+ tuples[*num_copied_tuples].value.v =
+ le32_to_cpu(elem->value);
+ }
+ found++;
+ (*num_copied_tuples)++;
+
+ /* stop if there's no space for any more new tuples */
+ if (*num_copied_tuples == tuples_size)
+ return 0;
+ }
+ }
+
+ /* next array */
+ array = (struct snd_soc_tplg_vendor_array *)((u8 *)array + asize);
+ }
+
+ return 0;
+}
+
+/**
+ * sof_parse_string_tokens - Parse multiple sets of tokens
+ * @scomp: pointer to soc component
+ * @object: target ipc struct for parsed values
+ * @offset: offset within the object pointer
+ * @tokens: array of struct sof_topology_token containing the tokens to be matched
+ * @num_tokens: number of tokens in tokens array
+ * @array: source pointer to consecutive vendor arrays in topology
+ *
+ * This function parses multiple sets of string type tokens in vendor arrays
+ */
static int sof_parse_string_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array,
- size_t offset)
+ void *object, int offset,
+ const struct sof_topology_token *tokens, int num_tokens,
+ struct snd_soc_tplg_vendor_array *array)
{
struct snd_soc_tplg_vendor_string_elem *elem;
int found = 0;
@@ -873,7 +585,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp,
elem = &array->string[i];
/* search for token */
- for (j = 0; j < count; j++) {
+ for (j = 0; j < num_tokens; j++) {
/* match token type */
if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING)
continue;
@@ -883,9 +595,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp,
continue;
/* matched - now load token */
- tokens[j].get_token(elem, object,
- offset + tokens[j].offset,
- tokens[j].size);
+ tokens[j].get_token(elem->string, object, offset + tokens[j].offset);
found++;
}
@@ -894,12 +604,21 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp,
return found;
}
+/**
+ * sof_parse_word_tokens - Parse multiple sets of tokens
+ * @scomp: pointer to soc component
+ * @object: target ipc struct for parsed values
+ * @offset: offset within the object pointer
+ * @tokens: array of struct sof_topology_token containing the tokens to be matched
+ * @num_tokens: number of tokens in tokens array
+ * @array: source pointer to consecutive vendor arrays in topology
+ *
+ * This function parses multiple sets of word type tokens in vendor arrays
+ */
static int sof_parse_word_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array,
- size_t offset)
+ void *object, int offset,
+ const struct sof_topology_token *tokens, int num_tokens,
+ struct snd_soc_tplg_vendor_array *array)
{
struct snd_soc_tplg_vendor_value_elem *elem;
int found = 0;
@@ -910,7 +629,7 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp,
elem = &array->value[i];
/* search for token */
- for (j = 0; j < count; j++) {
+ for (j = 0; j < num_tokens; j++) {
/* match token type */
if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
@@ -923,9 +642,7 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp,
continue;
/* load token */
- tokens[j].get_token(elem, object,
- offset + tokens[j].offset,
- tokens[j].size);
+ tokens[j].get_token(elem, object, offset + tokens[j].offset);
found++;
}
@@ -940,27 +657,26 @@ static int sof_parse_word_tokens(struct snd_soc_component *scomp,
* @object: target ipc struct for parsed values
* @tokens: token definition array describing what tokens to parse
* @count: number of tokens in definition array
- * @array: source pointer to consecutive vendor arrays to be parsed
- * @priv_size: total size of the consecutive source arrays
- * @sets: number of similar token sets to be parsed, 1 set has count elements
+ * @array: source pointer to consecutive vendor arrays in topology
+ * @array_size: total size of @array
+ * @token_instance_num: number of times the same tokens needs to be parsed i.e. the function
+ * looks for @token_instance_num of each token in the @tokens
* @object_size: offset to next target ipc struct with multiple sets
*
* This function parses multiple sets of tokens in vendor arrays into
* consecutive ipc structs.
*/
static int sof_parse_token_sets(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array,
- int priv_size, int sets, size_t object_size)
+ void *object, const struct sof_topology_token *tokens,
+ int count, struct snd_soc_tplg_vendor_array *array,
+ int array_size, int token_instance_num, size_t object_size)
{
size_t offset = 0;
int found = 0;
int total = 0;
int asize;
- while (priv_size > 0 && total < count * sets) {
+ while (array_size > 0 && total < count * token_instance_num) {
asize = le32_to_cpu(array->size);
/* validate asize */
@@ -971,8 +687,8 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
}
/* make sure there is enough data before parsing */
- priv_size -= asize;
- if (priv_size < 0) {
+ array_size -= asize;
+ if (array_size < 0) {
dev_err(scomp->dev, "error: invalid array size 0x%x\n",
asize);
return -EINVAL;
@@ -981,19 +697,19 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
/* call correct parser depending on type */
switch (le32_to_cpu(array->type)) {
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
- found += sof_parse_uuid_tokens(scomp, object, tokens,
- count, array, offset);
+ found += sof_parse_uuid_tokens(scomp, object, offset, tokens, count,
+ array);
break;
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
- found += sof_parse_string_tokens(scomp, object, tokens,
- count, array, offset);
+ found += sof_parse_string_tokens(scomp, object, offset, tokens, count,
+ array);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
case SND_SOC_TPLG_TUPLE_TYPE_WORD:
case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
- found += sof_parse_word_tokens(scomp, object, tokens,
- count, array, offset);
+ found += sof_parse_word_tokens(scomp, object, offset, tokens, count,
+ array);
break;
default:
dev_err(scomp->dev, "error: unknown token type %d\n",
@@ -1016,12 +732,23 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
return 0;
}
-static int sof_parse_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
+/**
+ * sof_parse_tokens - Parse one set of tokens
+ * @scomp: pointer to soc component
+ * @object: target ipc struct for parsed values
+ * @tokens: token definition array describing what tokens to parse
+ * @num_tokens: number of tokens in definition array
+ * @array: source pointer to consecutive vendor arrays in topology
+ * @array_size: total size of @array
+ *
+ * This function parses a single set of tokens in vendor arrays into
+ * consecutive ipc structs.
+ */
+static int sof_parse_tokens(struct snd_soc_component *scomp, void *object,
+ const struct sof_topology_token *tokens, int num_tokens,
struct snd_soc_tplg_vendor_array *array,
- int priv_size)
+ int array_size)
+
{
/*
* sof_parse_tokens is used when topology contains only a single set of
@@ -1029,16 +756,8 @@ static int sof_parse_tokens(struct snd_soc_component *scomp,
* sof_parse_token_sets are sets = 1 (only 1 set) and
* object_size = 0 (irrelevant).
*/
- return sof_parse_token_sets(scomp, object, tokens, count, array,
- priv_size, 1, 0);
-}
-
-static void sof_dbg_comp_config(struct snd_soc_component *scomp,
- struct sof_ipc_comp_config *config)
-{
- dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n",
- config->periods_sink, config->periods_source,
- config->frame_fmt);
+ return sof_parse_token_sets(scomp, object, tokens, num_tokens, array,
+ array_size, 1, 0);
}
/*
@@ -1053,16 +772,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_mixer_control *mc =
container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);
- struct sof_ipc_ctrl_data *cdata;
int tlv[TLV_ITEMS];
- unsigned int i;
int ret;
/* validate topology data */
- if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) {
- ret = -EINVAL;
- goto out;
- }
+ if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN)
+ return -EINVAL;
/*
* If control has more than 2 channels we need to override the info. This is because even if
@@ -1073,48 +788,26 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,
if (le32_to_cpu(mc->num_channels) > 2)
kc->info = snd_sof_volume_info;
- /* init the volume get/put data */
- scontrol->size = struct_size(scontrol->control_data, chanv,
- le32_to_cpu(mc->num_channels));
- scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
- if (!scontrol->control_data) {
- ret = -ENOMEM;
- goto out;
- }
-
scontrol->comp_id = sdev->next_comp_id;
scontrol->min_volume_step = le32_to_cpu(mc->min);
scontrol->max_volume_step = le32_to_cpu(mc->max);
scontrol->num_channels = le32_to_cpu(mc->num_channels);
- scontrol->control_data->index = kc->index;
- /* set cmd for mixer control */
- if (le32_to_cpu(mc->max) == 1) {
- scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH;
+ scontrol->max = le32_to_cpu(mc->max);
+ if (le32_to_cpu(mc->max) == 1)
goto skip;
- }
-
- scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME;
/* extract tlv data */
if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) {
dev_err(scomp->dev, "error: invalid TLV data\n");
- ret = -EINVAL;
- goto out_free;
+ return -EINVAL;
}
/* set up volume table */
ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
if (ret < 0) {
dev_err(scomp->dev, "error: setting up volume table\n");
- goto out_free;
- }
-
- /* set default volume values to 0dB in control */
- cdata = scontrol->control_data;
- for (i = 0; i < scontrol->num_channels; i++) {
- cdata->chanv[i].channel = i;
- cdata->chanv[i].value = VOL_ZERO_DB;
+ return ret;
}
skip:
@@ -1125,7 +818,7 @@ skip:
if (ret != 0) {
dev_err(scomp->dev, "error: parse led tokens failed %d\n",
le32_to_cpu(mc->priv.size));
- goto out_free_table;
+ goto err;
}
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
@@ -1133,12 +826,10 @@ skip:
return 0;
-out_free_table:
+err:
if (le32_to_cpu(mc->max) > 1)
kfree(scontrol->volume_table);
-out_free:
- kfree(scontrol->control_data);
-out:
+
return ret;
}
@@ -1155,17 +846,8 @@ static int sof_control_load_enum(struct snd_soc_component *scomp,
if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN)
return -EINVAL;
- /* init the enum get/put data */
- scontrol->size = struct_size(scontrol->control_data, chanv,
- le32_to_cpu(ec->num_channels));
- scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
- if (!scontrol->control_data)
- return -ENOMEM;
-
scontrol->comp_id = sdev->next_comp_id;
scontrol->num_channels = le32_to_cpu(ec->num_channels);
- scontrol->control_data->index = kc->index;
- scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM;
dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
@@ -1179,77 +861,26 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
struct snd_soc_tplg_ctl_hdr *hdr)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_ctrl_data *cdata;
struct snd_soc_tplg_bytes_control *control =
container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
- size_t max_size = sbe->max;
size_t priv_size = le32_to_cpu(control->priv.size);
- int ret;
-
- if (max_size < sizeof(struct sof_ipc_ctrl_data) ||
- max_size < sizeof(struct sof_abi_hdr)) {
- ret = -EINVAL;
- goto out;
- }
-
- /* init the get/put bytes data */
- if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) {
- dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n",
- priv_size, max_size - sizeof(struct sof_ipc_ctrl_data));
- ret = -EINVAL;
- goto out;
- }
-
- scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size;
-
- scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
- cdata = scontrol->control_data;
- if (!scontrol->control_data) {
- ret = -ENOMEM;
- goto out;
- }
+ scontrol->max_size = sbe->max;
scontrol->comp_id = sdev->next_comp_id;
- scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY;
- scontrol->control_data->index = kc->index;
- dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
- scontrol->comp_id, scontrol->num_channels);
+ dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id);
- if (le32_to_cpu(control->priv.size) > 0) {
- memcpy(cdata->data, control->priv.data,
- le32_to_cpu(control->priv.size));
+ /* copy the private data */
+ if (priv_size > 0) {
+ scontrol->priv = kmemdup(control->priv.data, priv_size, GFP_KERNEL);
+ if (!scontrol->priv)
+ return -ENOMEM;
- if (cdata->data->magic != SOF_ABI_MAGIC) {
- dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n",
- cdata->data->magic);
- ret = -EINVAL;
- goto out_free;
- }
- if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
- cdata->data->abi)) {
- dev_err(scomp->dev,
- "error: Incompatible ABI version 0x%08x.\n",
- cdata->data->abi);
- ret = -EINVAL;
- goto out_free;
- }
- if (cdata->data->size + sizeof(struct sof_abi_hdr) !=
- le32_to_cpu(control->priv.size)) {
- dev_err(scomp->dev,
- "error: Conflict in bytes vs. priv size.\n");
- ret = -EINVAL;
- goto out_free;
- }
+ scontrol->priv_size = priv_size;
}
return 0;
-
-out_free:
- kfree(scontrol->control_data);
-out:
- return ret;
}
/* external kcontrol init - used for any driver specific init */
@@ -1272,8 +903,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index,
if (!scontrol)
return -ENOMEM;
+ scontrol->name = kstrdup(hdr->name, GFP_KERNEL);
+ if (!scontrol->name)
+ return -ENOMEM;
+
scontrol->scomp = scomp;
scontrol->access = kc->access;
+ scontrol->info_type = le32_to_cpu(hdr->ops.info);
+ scontrol->index = kc->index;
switch (le32_to_cpu(hdr->ops.info)) {
case SND_SOC_TPLG_CTL_VOLSW:
@@ -1324,22 +961,26 @@ 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);
- struct sof_ipc_free fcomp;
+ const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
struct snd_sof_control *scontrol = dobj->private;
+ int ret = 0;
- dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name);
+ dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name);
- fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;
- fcomp.hdr.size = sizeof(fcomp);
- fcomp.id = scontrol->comp_id;
+ if (ipc_tplg_ops->control_free) {
+ ret = ipc_tplg_ops->control_free(sdev, scontrol);
+ if (ret < 0)
+ dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name);
+ }
- kfree(scontrol->control_data);
+ /* free all data before returning in case of error too */
+ kfree(scontrol->ipc_control_data);
+ kfree(scontrol->priv);
+ kfree(scontrol->name);
list_del(&scontrol->list);
kfree(scontrol);
- /* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc,
- fcomp.hdr.cmd, &fcomp, sizeof(fcomp),
- NULL, 0);
+
+ return ret;
}
/*
@@ -1427,148 +1068,6 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
return 0;
}
-/**
- * sof_comp_alloc - allocate and initialize buffer for a new component
- * @swidget: pointer to struct snd_sof_widget containing extended data
- * @ipc_size: IPC payload size that will be updated depending on valid
- * extended data.
- * @index: ID of the pipeline the component belongs to
- *
- * Return: The pointer to the new allocated component, NULL if failed.
- */
-static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget,
- size_t *ipc_size, int index)
-{
- u8 nil_uuid[SOF_UUID_SIZE] = {0};
- struct sof_ipc_comp *comp;
- size_t total_size = *ipc_size;
-
- /* only non-zero UUID is valid */
- if (memcmp(&swidget->comp_ext, nil_uuid, SOF_UUID_SIZE))
- total_size += sizeof(swidget->comp_ext);
-
- comp = kzalloc(total_size, GFP_KERNEL);
- if (!comp)
- return NULL;
-
- /* configure comp new IPC message */
- comp->hdr.size = total_size;
- comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW;
- comp->id = swidget->comp_id;
- comp->pipeline_id = index;
- comp->core = swidget->core;
-
- /* handle the extended data if needed */
- if (total_size > *ipc_size) {
- /* append extended data to the end of the component */
- memcpy((u8 *)comp + *ipc_size, &swidget->comp_ext, sizeof(swidget->comp_ext));
- comp->ext_data_length = sizeof(swidget->comp_ext);
- }
-
- /* update ipc_size and return */
- *ipc_size = total_size;
- return comp;
-}
-
-static int sof_widget_load_dai(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- struct snd_sof_dai *dai)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_dai *comp_dai;
- size_t ipc_size = sizeof(*comp_dai);
- int ret;
-
- comp_dai = (struct sof_ipc_comp_dai *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!comp_dai)
- return -ENOMEM;
-
- /* configure dai IPC message */
- comp_dai->comp.type = SOF_COMP_DAI;
- comp_dai->config.hdr.size = sizeof(comp_dai->config);
-
- ret = sof_parse_tokens(scomp, comp_dai, dai_tokens,
- ARRAY_SIZE(dai_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse dai tokens failed %d\n",
- le32_to_cpu(private->size));
- goto finish;
- }
-
- ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n",
- private->size);
- goto finish;
- }
-
- dev_dbg(scomp->dev, "dai %s: type %d index %d\n",
- swidget->widget->name, comp_dai->type, comp_dai->dai_index);
- sof_dbg_comp_config(scomp, &comp_dai->config);
-
- if (dai) {
- dai->scomp = scomp;
-
- /*
- * copy only the sof_ipc_comp_dai to avoid collapsing
- * the snd_sof_dai, the extended data is kept in the
- * snd_sof_widget.
- */
- memcpy(&dai->comp_dai, comp_dai, sizeof(*comp_dai));
- }
-
-finish:
- kfree(comp_dai);
- return ret;
-}
-
-/*
- * Buffer topology
- */
-
-static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_buffer *buffer;
- int ret;
-
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- /* configure dai IPC message */
- buffer->comp.hdr.size = sizeof(*buffer);
- buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW;
- buffer->comp.id = swidget->comp_id;
- buffer->comp.type = SOF_COMP_BUFFER;
- buffer->comp.pipeline_id = index;
- buffer->comp.core = swidget->core;
-
- ret = sof_parse_tokens(scomp, buffer, buffer_tokens,
- ARRAY_SIZE(buffer_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse buffer tokens failed %d\n",
- private->size);
- kfree(buffer);
- return ret;
- }
-
- dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n",
- swidget->widget->name, buffer->size, buffer->caps);
-
- swidget->private = buffer;
-
- return 0;
-}
-
/* bind PCM ID to host component ID */
static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
int dir)
@@ -1588,670 +1087,87 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
return 0;
}
-/*
- * PCM Topology
- */
-
-static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- enum sof_ipc_stream_direction dir,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_host *host;
- size_t ipc_size = sizeof(*host);
- int ret;
-
- host = (struct sof_ipc_comp_host *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!host)
- return -ENOMEM;
-
- /* configure host comp IPC message */
- host->comp.type = SOF_COMP_HOST;
- host->direction = dir;
- host->config.hdr.size = sizeof(host->config);
-
- ret = sof_parse_tokens(scomp, host, pcm_tokens,
- ARRAY_SIZE(pcm_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse host tokens failed %d\n",
- private->size);
- goto err;
- }
-
- ret = sof_parse_tokens(scomp, &host->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse host.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
-
- dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name);
- sof_dbg_comp_config(scomp, &host->config);
-
- swidget->private = host;
-
- return 0;
-err:
- kfree(host);
- return ret;
-}
-
-/*
- * Pipeline Topology
- */
-static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_pipe_new *pipeline;
- struct snd_sof_widget *comp_swidget;
- int ret;
-
- pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);
- if (!pipeline)
- return -ENOMEM;
-
- /* configure dai IPC message */
- pipeline->hdr.size = sizeof(*pipeline);
- pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW;
- pipeline->pipeline_id = index;
- pipeline->comp_id = swidget->comp_id;
-
- /* component at start of pipeline is our stream id */
- comp_swidget = snd_sof_find_swidget(scomp, tw->sname);
- if (!comp_swidget) {
- dev_err(scomp->dev, "error: widget %s refers to non existent widget %s\n",
- tw->name, tw->sname);
- ret = -EINVAL;
- goto err;
- }
-
- pipeline->sched_id = comp_swidget->comp_id;
-
- dev_dbg(scomp->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n",
- pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id);
-
- ret = sof_parse_tokens(scomp, pipeline, sched_tokens,
- ARRAY_SIZE(sched_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse pipeline tokens failed %d\n",
- private->size);
- goto err;
- }
-
- ret = sof_parse_tokens(scomp, swidget, pipeline_tokens,
- ARRAY_SIZE(pipeline_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n",
- private->size);
- goto err;
- }
-
- if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
- pipeline->core = SOF_DSP_PRIMARY_CORE;
-
- if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))
- swidget->dynamic_pipeline_widget =
- sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE);
-
- dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n",
- swidget->widget->name, pipeline->period, pipeline->priority,
- pipeline->period_mips, pipeline->core, pipeline->frames_per_sched,
- swidget->dynamic_pipeline_widget);
-
- swidget->private = pipeline;
-
- return 0;
-err:
- kfree(pipeline);
- return ret;
-}
-
-/*
- * Mixer topology
- */
-
-static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_mixer *mixer;
- size_t ipc_size = sizeof(*mixer);
- int ret;
-
- mixer = (struct sof_ipc_comp_mixer *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!mixer)
- return -ENOMEM;
-
- /* configure mixer IPC message */
- mixer->comp.type = SOF_COMP_MIXER;
- mixer->config.hdr.size = sizeof(mixer->config);
-
- ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse mixer.cfg tokens failed %d\n",
- private->size);
- kfree(mixer);
- return ret;
- }
-
- sof_dbg_comp_config(scomp, &mixer->config);
-
- swidget->private = mixer;
-
- return 0;
-}
-
-/*
- * Mux topology
- */
-static int sof_widget_load_mux(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_mux *mux;
- size_t ipc_size = sizeof(*mux);
- int ret;
-
- mux = (struct sof_ipc_comp_mux *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!mux)
- return -ENOMEM;
-
- /* configure mux IPC message */
- mux->comp.type = SOF_COMP_MUX;
- mux->config.hdr.size = sizeof(mux->config);
-
- ret = sof_parse_tokens(scomp, &mux->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse mux.cfg tokens failed %d\n",
- private->size);
- kfree(mux);
- return ret;
- }
-
- sof_dbg_comp_config(scomp, &mux->config);
-
- swidget->private = mux;
-
- return 0;
-}
-
-/*
- * PGA Topology
- */
-
-static int sof_widget_load_pga(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
+static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_sof_widget *swidget,
+ struct snd_soc_tplg_dapm_widget *tw,
+ 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;
struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_volume *volume;
- struct snd_sof_control *scontrol;
- size_t ipc_size = sizeof(*volume);
- int min_step;
- int max_step;
- int ret;
-
- volume = (struct sof_ipc_comp_volume *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!volume)
- return -ENOMEM;
-
- if (!le32_to_cpu(tw->num_kcontrols)) {
- dev_err(scomp->dev, "error: invalid kcontrol count %d for volume\n",
- tw->num_kcontrols);
- ret = -EINVAL;
- goto err;
- }
-
- /* configure volume IPC message */
- volume->comp.type = SOF_COMP_VOLUME;
- volume->config.hdr.size = sizeof(volume->config);
-
- ret = sof_parse_tokens(scomp, volume, volume_tokens,
- ARRAY_SIZE(volume_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse volume tokens failed %d\n",
- private->size);
- goto err;
- }
- ret = sof_parse_tokens(scomp, &volume->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse volume.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
-
- sof_dbg_comp_config(scomp, &volume->config);
-
- swidget->private = volume;
-
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- if (scontrol->comp_id == swidget->comp_id &&
- scontrol->volume_table) {
- min_step = scontrol->min_volume_step;
- max_step = scontrol->max_volume_step;
- volume->min_value = scontrol->volume_table[min_step];
- volume->max_value = scontrol->volume_table[max_step];
- volume->channels = scontrol->num_channels;
- break;
- }
- }
-
- return 0;
-err:
- kfree(volume);
- return ret;
-}
-
-/*
- * SRC Topology
- */
-
-static int sof_widget_load_src(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_src *src;
- size_t ipc_size = sizeof(*src);
- int ret;
-
- src = (struct sof_ipc_comp_src *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!src)
- return -ENOMEM;
-
- /* configure src IPC message */
- src->comp.type = SOF_COMP_SRC;
- src->config.hdr.size = sizeof(src->config);
-
- ret = sof_parse_tokens(scomp, src, src_tokens,
- ARRAY_SIZE(src_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse src tokens failed %d\n",
- private->size);
- goto err;
- }
-
- ret = sof_parse_tokens(scomp, &src->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse src.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
+ int num_tuples = 0;
+ size_t size;
+ int ret, i;
- dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n",
- swidget->widget->name, src->source_rate, src->sink_rate);
- sof_dbg_comp_config(scomp, &src->config);
-
- swidget->private = src;
-
- return 0;
-err:
- kfree(src);
- return ret;
-}
-
-/*
- * ASRC Topology
- */
-
-static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_asrc *asrc;
- size_t ipc_size = sizeof(*asrc);
- int ret;
-
- asrc = (struct sof_ipc_comp_asrc *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!asrc)
- return -ENOMEM;
-
- /* configure ASRC IPC message */
- asrc->comp.type = SOF_COMP_ASRC;
- asrc->config.hdr.size = sizeof(asrc->config);
-
- ret = sof_parse_tokens(scomp, asrc, asrc_tokens,
- ARRAY_SIZE(asrc_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse asrc tokens failed %d\n",
- private->size);
- goto err;
- }
-
- ret = sof_parse_tokens(scomp, &asrc->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse asrc.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
+ if (count > 0 && !object_token_list) {
+ dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name);
+ return -EINVAL;
}
- dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d "
- "asynch %d operation %d\n",
- swidget->widget->name, asrc->source_rate, asrc->sink_rate,
- asrc->asynchronous_mode, asrc->operation_mode);
- sof_dbg_comp_config(scomp, &asrc->config);
-
- swidget->private = asrc;
-
- return 0;
-err:
- kfree(asrc);
- return ret;
-}
-
-/*
- * Signal Generator Topology
- */
-
-static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
-{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_tone *tone;
- size_t ipc_size = sizeof(*tone);
- int ret;
+ /* calculate max size of tuples array */
+ for (i = 0; i < count; i++)
+ num_tuples += token_list[object_token_list[i]].count;
- tone = (struct sof_ipc_comp_tone *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!tone)
+ /* allocate memory for tuples array */
+ size = sizeof(struct snd_sof_tuple) * num_tuples;
+ swidget->tuples = kzalloc(size, GFP_KERNEL);
+ if (!swidget->tuples)
return -ENOMEM;
- /* configure siggen IPC message */
- tone->comp.type = SOF_COMP_TONE;
- tone->config.hdr.size = sizeof(tone->config);
-
- ret = sof_parse_tokens(scomp, tone, tone_tokens,
- ARRAY_SIZE(tone_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse tone tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
-
- ret = sof_parse_tokens(scomp, &tone->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse tone.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
-
- dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n",
- swidget->widget->name, tone->frequency, tone->amplitude);
- sof_dbg_comp_config(scomp, &tone->config);
-
- swidget->private = tone;
-
- return 0;
-err:
- kfree(tone);
- return ret;
-}
-
-static int sof_get_control_data(struct snd_soc_component *scomp,
- struct snd_soc_dapm_widget *widget,
- struct sof_widget_data *wdata,
- size_t *size)
-{
- const struct snd_kcontrol_new *kc;
- struct soc_mixer_control *sm;
- struct soc_bytes_ext *sbe;
- struct soc_enum *se;
- int i;
-
- *size = 0;
-
- for (i = 0; i < widget->num_kcontrols; i++) {
- kc = &widget->kcontrol_news[i];
-
- switch (widget->dobj.widget.kcontrol_type[i]) {
- case SND_SOC_TPLG_TYPE_MIXER:
- sm = (struct soc_mixer_control *)kc->private_value;
- wdata[i].control = sm->dobj.private;
- break;
- case SND_SOC_TPLG_TYPE_BYTES:
- sbe = (struct soc_bytes_ext *)kc->private_value;
- wdata[i].control = sbe->dobj.private;
- break;
- case SND_SOC_TPLG_TYPE_ENUM:
- se = (struct soc_enum *)kc->private_value;
- wdata[i].control = se->dobj.private;
- break;
- default:
- dev_err(scomp->dev, "error: unknown kcontrol type %u in widget %s\n",
- widget->dobj.widget.kcontrol_type[i],
- widget->name);
- return -EINVAL;
- }
-
- if (!wdata[i].control) {
- dev_err(scomp->dev, "error: no scontrol for widget %s\n",
- widget->name);
- return -EINVAL;
+ /* parse token list for widget */
+ for (i = 0; i < count; i++) {
+ if (object_token_list[i] >= SOF_TOKEN_COUNT) {
+ dev_err(scomp->dev, "Invalid token id %d for widget %s\n",
+ object_token_list[i], swidget->widget->name);
+ ret = -EINVAL;
+ goto err;
}
- wdata[i].pdata = wdata[i].control->control_data->data;
- if (!wdata[i].pdata)
- return -EINVAL;
-
- /* make sure data is valid - data can be updated at runtime */
- if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES &&
- wdata[i].pdata->magic != SOF_ABI_MAGIC)
- return -EINVAL;
-
- *size += wdata[i].pdata->size;
+ /* parse and save UUID in swidget */
+ if (object_token_list[i] == SOF_COMP_EXT_TOKENS) {
+ ret = sof_parse_tokens(scomp, swidget,
+ token_list[object_token_list[i]].tokens,
+ token_list[object_token_list[i]].count,
+ private->array, le32_to_cpu(private->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed parsing %s for widget %s\n",
+ token_list[object_token_list[i]].name,
+ swidget->widget->name);
+ goto err;
+ }
- /* get data type */
- switch (wdata[i].control->control_data->cmd) {
- case SOF_CTRL_CMD_VOLUME:
- case SOF_CTRL_CMD_ENUM:
- case SOF_CTRL_CMD_SWITCH:
- wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE;
- wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;
- break;
- case SOF_CTRL_CMD_BINARY:
- wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA;
- wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET;
- break;
- default:
- break;
+ continue;
}
- }
-
- return 0;
-}
-
-static int sof_process_load(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw,
- int type)
-{
- struct snd_soc_dapm_widget *widget = swidget->widget;
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_process *process;
- struct sof_widget_data *wdata = NULL;
- size_t ipc_data_size = 0;
- size_t ipc_size;
- int offset = 0;
- int ret;
- int i;
-
- /* allocate struct for widget control data sizes and types */
- if (widget->num_kcontrols) {
- wdata = kcalloc(widget->num_kcontrols,
- sizeof(*wdata),
- GFP_KERNEL);
-
- if (!wdata)
- return -ENOMEM;
-
- /* get possible component controls and get size of all pdata */
- ret = sof_get_control_data(scomp, widget, wdata,
- &ipc_data_size);
- if (ret < 0)
- goto out;
- }
-
- ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size;
-
- /* we are exceeding max ipc size, config needs to be sent separately */
- if (ipc_size > SOF_IPC_MSG_MAX_SIZE) {
- ipc_size -= ipc_data_size;
- ipc_data_size = 0;
- }
-
- process = (struct sof_ipc_comp_process *)
- sof_comp_alloc(swidget, &ipc_size, index);
- if (!process) {
- ret = -ENOMEM;
- goto out;
- }
-
- /* configure iir IPC message */
- process->comp.type = type;
- process->config.hdr.size = sizeof(process->config);
-
- ret = sof_parse_tokens(scomp, &process->config, comp_tokens,
- ARRAY_SIZE(comp_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse process.cfg tokens failed %d\n",
- le32_to_cpu(private->size));
- goto err;
- }
-
- sof_dbg_comp_config(scomp, &process->config);
-
- /*
- * found private data in control, so copy it.
- * get possible component controls - get size of all pdata,
- * then memcpy with headers
- */
- if (ipc_data_size) {
- for (i = 0; i < widget->num_kcontrols; i++) {
- memcpy(&process->data + offset,
- wdata[i].pdata->data,
- wdata[i].pdata->size);
- offset += wdata[i].pdata->size;
+ /* copy one set of tuples per token ID into swidget->tuples */
+ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+ object_token_list[i], 1, swidget->tuples,
+ num_tuples, &swidget->num_tuples);
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed parsing %s for widget %s err: %d\n",
+ token_list[object_token_list[i]].name, swidget->widget->name, ret);
+ goto err;
}
}
- process->size = ipc_data_size;
- swidget->private = process;
+ return 0;
err:
- if (ret < 0)
- kfree(process);
-out:
- kfree(wdata);
+ kfree(swidget->tuples);
return ret;
}
-/*
- * Processing Component Topology - can be "effect", "codec", or general
- * "processing".
- */
-
-static int sof_widget_load_process(struct snd_soc_component *scomp, int index,
- struct snd_sof_widget *swidget,
- struct snd_soc_tplg_dapm_widget *tw)
+static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples)
{
- struct snd_soc_tplg_private *private = &tw->priv;
- struct sof_ipc_comp_process config;
- int ret;
+ int i;
- /* check we have some tokens - we need at least process type */
- if (le32_to_cpu(private->size) == 0) {
- dev_err(scomp->dev, "error: process tokens not found\n");
+ if (!tuples)
return -EINVAL;
- }
-
- memset(&config, 0, sizeof(config));
- config.comp.core = swidget->core;
-
- /* get the process token */
- ret = sof_parse_tokens(scomp, &config, process_tokens,
- ARRAY_SIZE(process_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse process tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- /* now load process specific data and send IPC */
- ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type));
- if (ret < 0) {
- dev_err(scomp->dev, "error: process loading failed\n");
- return ret;
- }
-
- return 0;
-}
-
-static int sof_widget_bind_event(struct snd_soc_component *scomp,
- struct snd_sof_widget *swidget,
- u16 event_type)
-{
- struct sof_ipc_comp *ipc_comp;
- /* validate widget event type */
- switch (event_type) {
- case SOF_KEYWORD_DETECT_DAPM_EVENT:
- /* only KEYWORD_DETECT comps should handle this */
- if (swidget->id != snd_soc_dapm_effect)
- break;
-
- ipc_comp = swidget->private;
- if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT)
- break;
-
- /* bind event to keyword detect comp */
- return snd_soc_tplg_widget_bind_event(swidget->widget,
- sof_kwd_events,
- ARRAY_SIZE(sof_kwd_events),
- event_type);
- default:
- break;
+ for (i = 0; i < num_tuples; i++) {
+ if (tuples[i].token == token_id)
+ return tuples[i].value.v;
}
- dev_err(scomp->dev,
- "error: invalid event type %d for widget %s\n",
- event_type, swidget->widget->name);
return -EINVAL;
}
@@ -2261,11 +1177,12 @@ 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;
struct snd_sof_widget *swidget;
struct snd_sof_dai *dai;
- struct sof_ipc_comp comp = {
- .core = SOF_DSP_PRIMARY_CORE,
- };
+ enum sof_tokens *token_list;
+ int token_list_size;
int ret = 0;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
@@ -2285,30 +1202,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
? tw->sname : "none");
- ret = sof_parse_tokens(scomp, &comp, core_tokens,
- ARRAY_SIZE(core_tokens), tw->priv.array,
- le32_to_cpu(tw->priv.size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parsing core tokens failed %d\n",
- ret);
- kfree(swidget);
- return ret;
- }
-
- if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
- comp.core = SOF_DSP_PRIMARY_CORE;
-
- swidget->core = comp.core;
-
- ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens,
- ARRAY_SIZE(comp_ext_tokens), tw->priv.array,
- le32_to_cpu(tw->priv.size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parsing comp_ext_tokens failed %d\n",
- ret);
- kfree(swidget);
- return ret;
- }
+ 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) {
@@ -2318,9 +1213,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
if (!dai) {
kfree(swidget);
return -ENOMEM;
+
}
- ret = sof_widget_load_dai(scomp, index, swidget, tw, dai);
+ ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size);
if (!ret)
ret = sof_connect_dai_widget(scomp, w, tw, dai);
if (ret < 0) {
@@ -2330,41 +1226,35 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
list_add(&dai->list, &sdev->dai_list);
swidget->private = dai;
break;
- case snd_soc_dapm_mixer:
- ret = sof_widget_load_mixer(scomp, index, swidget, tw);
+ case snd_soc_dapm_effect:
+ /* check we have some tokens - we need at least process type */
+ if (le32_to_cpu(tw->priv.size) == 0) {
+ dev_err(scomp->dev, "error: process tokens not found\n");
+ ret = -EINVAL;
+ break;
+ }
+ ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size);
break;
case snd_soc_dapm_pga:
- ret = sof_widget_load_pga(scomp, index, swidget, tw);
- break;
+ if (!le32_to_cpu(tw->num_kcontrols)) {
+ dev_err(scomp->dev, "invalid kcontrol count %d for volume\n",
+ tw->num_kcontrols);
+ ret = -EINVAL;
+ break;
+ }
+
+ fallthrough;
+ case snd_soc_dapm_mixer:
case snd_soc_dapm_buffer:
- ret = sof_widget_load_buffer(scomp, index, swidget, tw);
- break;
case snd_soc_dapm_scheduler:
- ret = sof_widget_load_pipeline(scomp, index, swidget, tw);
- break;
case snd_soc_dapm_aif_out:
- ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_CAPTURE, tw);
- break;
case snd_soc_dapm_aif_in:
- ret = sof_widget_load_pcm(scomp, index, swidget,
- SOF_IPC_STREAM_PLAYBACK, tw);
- break;
case snd_soc_dapm_src:
- ret = sof_widget_load_src(scomp, index, swidget, tw);
- break;
case snd_soc_dapm_asrc:
- ret = sof_widget_load_asrc(scomp, index, swidget, tw);
- break;
case snd_soc_dapm_siggen:
- ret = sof_widget_load_siggen(scomp, index, swidget, tw);
- break;
- case snd_soc_dapm_effect:
- ret = sof_widget_load_process(scomp, index, swidget, tw);
- break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
- ret = sof_widget_load_mux(scomp, index, swidget, tw);
+ ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size);
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_dai_link:
@@ -2374,7 +1264,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
break;
}
- /* check IPC reply */
+ if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) {
+ swidget->core = SOF_DSP_PRIMARY_CORE;
+ } else {
+ int core = sof_get_token_value(SOF_TKN_COMP_CORE_ID, swidget->tuples,
+ swidget->num_tuples);
+
+ if (core >= 0)
+ swidget->core = core;
+ }
+
+ /* check token parsing reply */
if (ret < 0) {
dev_err(scomp->dev,
"error: failed to add widget id %d type %d name : %s stream %s\n",
@@ -2387,13 +1287,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
/* bind widget to external event */
if (tw->event_type) {
- ret = sof_widget_bind_event(scomp, swidget,
- le16_to_cpu(tw->event_type));
- if (ret) {
- dev_err(scomp->dev, "error: widget event binding failed\n");
- kfree(swidget->private);
- kfree(swidget);
- return ret;
+ if (widget_ops[w->id].bind_event) {
+ ret = widget_ops[w->id].bind_event(scomp, swidget,
+ le16_to_cpu(tw->event_type));
+ if (ret) {
+ dev_err(scomp->dev, "widget event binding failed for %s\n",
+ swidget->widget->name);
+ kfree(swidget->private);
+ kfree(swidget->tuples);
+ kfree(swidget);
+ return ret;
+ }
}
}
@@ -2422,6 +1326,9 @@ static int sof_route_unload(struct snd_soc_component *scomp,
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 snd_kcontrol_new *kc;
struct snd_soc_dapm_widget *widget;
struct snd_sof_control *scontrol;
@@ -2444,11 +1351,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
case snd_soc_dapm_dai_out:
dai = swidget->private;
- if (dai) {
- /* free dai config */
- kfree(dai->dai_config);
+ if (dai)
list_del(&dai->list);
- }
break;
default:
break;
@@ -2474,14 +1378,17 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
dev_warn(scomp->dev, "unsupported kcontrol_type\n");
goto out;
}
- kfree(scontrol->control_data);
+ kfree(scontrol->ipc_control_data);
list_del(&scontrol->list);
kfree(scontrol);
}
out:
- /* free private value */
- kfree(swidget->private);
+ /* free IPC related data */
+ if (widget_ops[swidget->id].ipc_free)
+ widget_ops[swidget->id].ipc_free(swidget);
+
+ kfree(swidget->tuples);
/* remove and free swidget object */
list_del(&swidget->list);
@@ -2626,602 +1533,24 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
return 0;
}
-static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- /* clock directions wrt codec */
- if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) {
- /* codec is bclk provider */
- if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)
- config->format |= SOF_DAI_FMT_CBP_CFP;
- else
- config->format |= SOF_DAI_FMT_CBP_CFC;
- } else {
- /* codec is bclk consumer */
- if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)
- config->format |= SOF_DAI_FMT_CBC_CFP;
- else
- config->format |= SOF_DAI_FMT_CBC_CFC;
- }
-
- /* inverted clocks ? */
- if (hw_config->invert_bclk) {
- if (hw_config->invert_fsync)
- config->format |= SOF_DAI_FMT_IB_IF;
- else
- config->format |= SOF_DAI_FMT_IB_NF;
- } else {
- if (hw_config->invert_fsync)
- config->format |= SOF_DAI_FMT_NB_IF;
- else
- config->format |= SOF_DAI_FMT_NB_NF;
- }
-}
-
-/*
- * Send IPC and set the same config for all DAIs with name matching the link
- * name. Note that the function can only be used for the case that all DAIs
- * have a common DAI config for now.
- */
-static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size,
- struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config,
- int num_conf, int curr_conf)
-{
- struct snd_sof_dai *dai;
- int found = 0;
- int i;
-
- list_for_each_entry(dai, &sdev->dai_list, list) {
- if (!dai->name)
- continue;
-
- if (strcmp(link->name, dai->name) == 0) {
- /*
- * the same dai config will be applied to all DAIs in
- * the same dai link. We have to ensure that the ipc
- * dai config's dai_index match to the component's
- * dai_index.
- */
- for (i = 0; i < num_conf; i++)
- config[i].dai_index = dai->comp_dai.dai_index;
-
- dev_dbg(sdev->dev, "set DAI config for %s index %d\n",
- dai->name, config[curr_conf].dai_index);
-
- dai->number_configs = num_conf;
- dai->current_config = curr_conf;
- dai->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL);
- if (!dai->dai_config)
- return -ENOMEM;
-
- found = 1;
- }
- }
-
- /*
- * machine driver may define a dai link with playback and capture
- * dai enabled, but the dai link in topology would support both, one
- * or none of them. Here print a warning message to notify user
- */
- if (!found) {
- dev_warn(sdev->dev, "warning: failed to find dai for dai link %s",
- link->name);
- }
-
- return 0;
-}
-
-static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
- struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config)
-{
- return sof_set_dai_config_multi(sdev, size, link, config, 1, 0);
-}
-
-static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config, int curr_conf)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- int num_conf = le32_to_cpu(cfg->num_hw_configs);
- u32 size = sizeof(*config);
- int ret;
- int i;
-
- /*
- * Parse common data, we should have 1 common data per hw_config.
- */
- ret = sof_parse_token_sets(scomp, &config->ssp, ssp_tokens,
- ARRAY_SIZE(ssp_tokens), private->array,
- le32_to_cpu(private->size),
- num_conf, size);
-
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse ssp tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- /* process all possible hw configs */
- for (i = 0; i < num_conf; i++) {
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(&hw_config[i], &config[i]);
-
- config[i].hdr.size = size;
-
- /* copy differentiating hw configs to ipc structs */
- config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate);
- config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate);
- config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate);
- config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots);
- config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width);
- config[i].ssp.mclk_direction = hw_config[i].mclk_direction;
- config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots);
- config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots);
-
- dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n",
- config[i].dai_index, config[i].format,
- config[i].ssp.mclk_rate, config[i].ssp.bclk_rate,
- config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits,
- config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots,
- config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control);
-
- /* validate SSP fsync rate and channel count */
- if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) {
- dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n",
- config[i].dai_index);
- return -EINVAL;
- }
-
- if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) {
- dev_err(scomp->dev, "error: invalid channel count for SSP%d\n",
- config[i].dai_index);
- return -EINVAL;
- }
- }
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config_multi(sdev, size, link, config, num_conf, curr_conf);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n",
- config->dai_index);
-
- return ret;
-}
-
-static int sof_link_sai_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- u32 size = sizeof(*config);
- int ret;
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->sai, 0, sizeof(struct sof_ipc_dai_sai_params));
- config->hdr.size = size;
-
- ret = sof_parse_tokens(scomp, &config->sai, sai_tokens,
- ARRAY_SIZE(sai_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse sai tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
- config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
- config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->sai.mclk_direction = hw_config->mclk_direction;
-
- config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
- config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
- config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots);
- config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots);
-
- dev_info(scomp->dev,
- "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
- config->dai_index, config->format,
- config->sai.mclk_rate, config->sai.tdm_slot_width,
- config->sai.tdm_slots, config->sai.mclk_id);
-
- if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) {
- dev_err(scomp->dev, "error: invalid channel count for SAI%d\n",
- config->dai_index);
- return -EINVAL;
- }
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to save DAI config for SAI%d\n",
- config->dai_index);
-
- return ret;
-}
-
-static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- u32 size = sizeof(*config);
- int ret;
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params));
- config->hdr.size = size;
-
- ret = sof_parse_tokens(scomp, &config->esai, esai_tokens,
- ARRAY_SIZE(esai_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse esai tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
- config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
- config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->esai.mclk_direction = hw_config->mclk_direction;
- config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
- config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
- config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);
- config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);
-
- dev_info(scomp->dev,
- "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
- config->dai_index, config->format,
- config->esai.mclk_rate, config->esai.tdm_slot_width,
- config->esai.tdm_slots, config->esai.mclk_id);
-
- if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {
- dev_err(scomp->dev, "error: invalid channel count for ESAI%d\n",
- config->dai_index);
- return -EINVAL;
- }
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to save DAI config for ESAI%d\n",
- config->dai_index);
-
- return ret;
-}
-
-static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- u32 size = sizeof(*config);
- int ret;
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params));
- config->hdr.size = size;
-
- config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
-
- dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n",
- config->dai_index, config->acpdmic.tdm_slots,
- config->acpdmic.fsync_rate);
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n",
- config->dai_index);
- return ret;
-}
-
-static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- u32 size = sizeof(*config);
- int ret;
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params));
- config->hdr.size = size;
-
- config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
-
- dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n",
- config->dai_index, config->acpbt.tdm_slots,
- config->acpbt.fsync_rate);
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n",
- config->dai_index);
- return ret;
-}
-
-static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- u32 size = sizeof(*config);
- int ret;
-
- /* handle master/slave and inverted clocks */
- sof_dai_set_format(hw_config, config);
-
- /* init IPC */
- memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params));
- config->hdr.size = size;
-
- config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
- config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
-
- dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n",
- config->dai_index, config->acpsp.tdm_slots,
- config->acpsp.fsync_rate);
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n",
- config->dai_index);
- return ret;
-}
-
-static int sof_link_afe_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- struct snd_soc_dai *dai;
- u32 size = sizeof(*config);
- int ret;
-
- config->hdr.size = size;
-
- /* get any bespoke DAI tokens */
- ret = sof_parse_tokens(scomp, &config->afe, afe_tokens,
- ARRAY_SIZE(afe_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "parse afe tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n",
- config->afe.rate, config->afe.channels, config->afe.format);
-
- dai = snd_soc_find_dai(link->cpus);
- if (!dai) {
- dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name);
- return -EINVAL;
- }
-
- config->afe.stream_id = DMA_CHAN_INVALID;
-
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "failed to process afe dai link %s", link->name);
-
- return ret;
-}
-
-static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
- struct sof_ipc_fw_version *v = &ready->version;
- size_t size = sizeof(*config);
- int ret, j;
-
- /* Ensure the entire DMIC config struct is zeros */
- memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params));
-
- /* get DMIC tokens */
- ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens,
- ARRAY_SIZE(dmic_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse dmic tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- /* get DMIC PDM tokens */
- ret = sof_parse_token_sets(scomp, &config->dmic.pdm[0], dmic_pdm_tokens,
- ARRAY_SIZE(dmic_pdm_tokens), private->array,
- le32_to_cpu(private->size),
- config->dmic.num_pdm_active,
- sizeof(struct sof_ipc_dai_dmic_pdm_ctrl));
-
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- /* set IPC header size */
- config->hdr.size = size;
-
- /* debug messages */
- dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n",
- config->dai_index, config->dmic.driver_ipc_version);
- dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n",
- config->dmic.pdmclk_min, config->dmic.pdmclk_max,
- config->dmic.duty_min);
- dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n",
- config->dmic.duty_max, config->dmic.fifo_fs,
- config->dmic.num_pdm_active);
- dev_dbg(scomp->dev, "fifo word length %hd\n", config->dmic.fifo_bits);
-
- for (j = 0; j < config->dmic.num_pdm_active; j++) {
- dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n",
- config->dmic.pdm[j].id,
- config->dmic.pdm[j].enable_mic_a,
- config->dmic.pdm[j].enable_mic_b);
- dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n",
- config->dmic.pdm[j].id,
- config->dmic.pdm[j].polarity_mic_a,
- config->dmic.pdm[j].polarity_mic_b);
- dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n",
- config->dmic.pdm[j].id,
- config->dmic.pdm[j].clk_edge,
- config->dmic.pdm[j].skew);
- }
-
- /*
- * this takes care of backwards compatible handling of fifo_bits_b.
- * It is deprecated since firmware ABI version 3.0.1.
- */
- if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1))
- config->dmic.fifo_bits_b = config->dmic.fifo_bits;
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n",
- config->dai_index);
-
- return ret;
-}
-
-static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- struct snd_soc_dai *dai;
- u32 size = sizeof(*config);
- int ret;
-
- /* init IPC */
- memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params));
- config->hdr.size = size;
-
- /* get any bespoke DAI tokens */
- ret = sof_parse_tokens(scomp, &config->hda, hda_tokens,
- ARRAY_SIZE(hda_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse hda tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- dev_dbg(scomp->dev, "HDA config rate %d channels %d\n",
- config->hda.rate, config->hda.channels);
-
- dai = snd_soc_find_dai(link->cpus);
- if (!dai) {
- dev_err(scomp->dev, "error: failed to find dai %s in %s",
- link->cpus->dai_name, __func__);
- return -EINVAL;
- }
-
- config->hda.link_dma_ch = DMA_CHAN_INVALID;
-
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to process hda dai link %s",
- link->name);
-
- return ret;
-}
-
-static int sof_link_alh_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
- struct snd_soc_tplg_link_config *cfg,
- struct snd_soc_tplg_hw_config *hw_config,
- struct sof_ipc_dai_config *config)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *private = &cfg->priv;
- u32 size = sizeof(*config);
- int ret;
-
- ret = sof_parse_tokens(scomp, &config->alh, alh_tokens,
- ARRAY_SIZE(alh_tokens), private->array,
- le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse alh tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
- }
-
- /* init IPC */
- config->hdr.size = size;
-
- /* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, config);
- if (ret < 0)
- dev_err(scomp->dev, "error: failed to save DAI config for ALH %d\n",
- config->dai_index);
-
- return ret;
-}
+static const struct sof_topology_token common_dai_link_tokens[] = {
+ {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
+ offsetof(struct snd_sof_dai_link, type)},
+};
/* DAI link - used for any driver specific init */
-static int sof_link_load(struct snd_soc_component *scomp, int index,
- struct snd_soc_dai_link *link,
+static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link,
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;
struct snd_soc_tplg_private *private = &cfg->priv;
- struct snd_soc_tplg_hw_config *hw_config;
- struct sof_ipc_dai_config common_config;
- struct sof_ipc_dai_config *config;
- int curr_conf;
- int num_conf;
- int ret;
- int i;
+ struct snd_sof_dai_link *slink;
+ size_t size;
+ u32 token_id = 0;
+ int num_tuples = 0;
+ int ret, num_sets;
if (!link->platforms) {
dev_err(scomp->dev, "error: no platforms\n");
@@ -3258,110 +1587,164 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- memset(&common_config, 0, sizeof(common_config));
+ slink = kzalloc(sizeof(*slink), GFP_KERNEL);
+ if (!slink)
+ return -ENOMEM;
- /* get any common DAI tokens */
- ret = sof_parse_tokens(scomp, &common_config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens),
- private->array, le32_to_cpu(private->size));
- if (ret != 0) {
- dev_err(scomp->dev, "error: parse link tokens failed %d\n",
- le32_to_cpu(private->size));
- return ret;
+ slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs);
+ slink->hw_configs = kmemdup(cfg->hw_config,
+ sizeof(*slink->hw_configs) * slink->num_hw_configs,
+ GFP_KERNEL);
+ if (!slink->hw_configs) {
+ kfree(slink);
+ return -ENOMEM;
}
- /*
- * DAI links are expected to have at least 1 hw_config.
- * But some older topologies might have no hw_config for HDA dai links.
- */
- hw_config = cfg->hw_config;
- num_conf = le32_to_cpu(cfg->num_hw_configs);
- if (!num_conf) {
- if (common_config.type != SOF_DAI_INTEL_HDA) {
- dev_err(scomp->dev, "error: unexpected DAI config count %d!\n",
- le32_to_cpu(cfg->num_hw_configs));
- return -EINVAL;
- }
- num_conf = 1;
- curr_conf = 0;
- } else {
- dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n",
- cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id));
+ slink->default_hw_cfg_id = le32_to_cpu(cfg->default_hw_config_id);
+ slink->link = link;
- for (curr_conf = 0; curr_conf < num_conf; curr_conf++) {
- if (hw_config[curr_conf].id == cfg->default_hw_config_id)
- break;
- }
+ dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d for dai link %s!\n",
+ slink->num_hw_configs, slink->default_hw_cfg_id, link->name);
- if (curr_conf == num_conf) {
- dev_err(scomp->dev, "error: default hw_config id: %d not found!\n",
- le32_to_cpu(cfg->default_hw_config_id));
- return -EINVAL;
- }
+ ret = sof_parse_tokens(scomp, slink, common_dai_link_tokens,
+ ARRAY_SIZE(common_dai_link_tokens),
+ private->array, le32_to_cpu(private->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n");
+ kfree(slink->hw_configs);
+ kfree(slink);
+ return ret;
}
- /* Reserve memory for all hw configs, eventually freed by widget */
- config = kcalloc(num_conf, sizeof(*config), GFP_KERNEL);
- if (!config)
- return -ENOMEM;
-
- /* Copy common data to all config ipc structs */
- for (i = 0; i < num_conf; i++) {
- config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
- config[i].format = le32_to_cpu(hw_config[i].fmt);
- config[i].type = common_config.type;
- config[i].dai_index = common_config.dai_index;
- }
+ if (!token_list)
+ goto out;
- /* now load DAI specific data and send IPC - type comes from token */
- switch (common_config.type) {
+ /* calculate size of tuples array */
+ num_tuples += token_list[SOF_DAI_LINK_TOKENS].count;
+ num_sets = slink->num_hw_configs;
+ switch (slink->type) {
case SOF_DAI_INTEL_SSP:
- ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, config, curr_conf);
+ token_id = SOF_SSP_TOKENS;
+ num_tuples += token_list[SOF_SSP_TOKENS].count * slink->num_hw_configs;
break;
case SOF_DAI_INTEL_DMIC:
- ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ token_id = SOF_DMIC_TOKENS;
+ num_tuples += token_list[SOF_DMIC_TOKENS].count;
+
+ /* Allocate memory for max PDM controllers */
+ num_tuples += token_list[SOF_DMIC_PDM_TOKENS].count * SOF_DAI_INTEL_DMIC_NUM_CTRL;
break;
case SOF_DAI_INTEL_HDA:
- ret = sof_link_hda_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ token_id = SOF_HDA_TOKENS;
+ num_tuples += token_list[SOF_HDA_TOKENS].count;
break;
case SOF_DAI_INTEL_ALH:
- ret = sof_link_alh_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ token_id = SOF_ALH_TOKENS;
+ num_tuples += token_list[SOF_ALH_TOKENS].count;
break;
case SOF_DAI_IMX_SAI:
- ret = sof_link_sai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ token_id = SOF_SAI_TOKENS;
+ num_tuples += token_list[SOF_SAI_TOKENS].count;
break;
case SOF_DAI_IMX_ESAI:
- ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config);
- break;
- case SOF_DAI_AMD_BT:
- ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config);
- break;
- case SOF_DAI_AMD_SP:
- ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config);
- break;
- case SOF_DAI_AMD_DMIC:
- ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf,
- config);
+ token_id = SOF_ESAI_TOKENS;
+ num_tuples += token_list[SOF_ESAI_TOKENS].count;
break;
case SOF_DAI_MEDIATEK_AFE:
- ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config);
+ token_id = SOF_AFE_TOKENS;
+ num_tuples += token_list[SOF_AFE_TOKENS].count;
break;
default:
- dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type);
- ret = -EINVAL;
break;
}
- kfree(config);
+ /* allocate memory for tuples array */
+ size = sizeof(struct snd_sof_tuple) * num_tuples;
+ slink->tuples = kzalloc(size, GFP_KERNEL);
+ if (!slink->tuples) {
+ kfree(slink->hw_configs);
+ kfree(slink);
+ return -ENOMEM;
+ }
+
+ /* parse one set of DAI link tokens */
+ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+ SOF_DAI_LINK_TOKENS, 1, slink->tuples,
+ num_tuples, &slink->num_tuples);
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
+ token_list[SOF_DAI_LINK_TOKENS].name, link->name);
+ goto err;
+ }
+
+ /* nothing more to do if there are no DAI type-specific tokens defined */
+ if (!token_id || !token_list[token_id].tokens)
+ goto out;
+
+ /* parse "num_sets" sets of DAI-specific tokens */
+ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+ token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples);
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
+ token_list[token_id].name, link->name);
+ goto err;
+ }
+
+ /* for DMIC, also parse all sets of DMIC PDM tokens based on active PDM count */
+ if (token_id == SOF_DMIC_TOKENS) {
+ num_sets = sof_get_token_value(SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE,
+ slink->tuples, slink->num_tuples);
+
+ if (num_sets < 0) {
+ dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name);
+ ret = num_sets;
+ goto err;
+ }
+
+ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
+ SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples,
+ num_tuples, &slink->num_tuples);
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse %s for dai link %s\n",
+ token_list[SOF_DMIC_PDM_TOKENS].name, link->name);
+ goto err;
+ }
+ }
+out:
+ link->dobj.private = slink;
+ list_add(&slink->list, &sdev->dai_link_list);
+
+ return 0;
+
+err:
+ kfree(slink->tuples);
+ kfree(slink->hw_configs);
+ kfree(slink);
return ret;
}
+static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj)
+{
+ struct snd_sof_dai_link *slink = dobj->private;
+
+ if (!slink)
+ return 0;
+
+ kfree(slink->tuples);
+ list_del(&slink->list);
+ kfree(slink->hw_configs);
+ kfree(slink);
+ dobj->private = NULL;
+
+ return 0;
+}
+
/* DAI link - used for any driver specific init */
static int sof_route_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_route *route)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_pipe_comp_connect *connect;
struct snd_sof_widget *source_swidget, *sink_swidget;
struct snd_soc_dobj *dobj = &route->dobj;
struct snd_sof_route *sroute;
@@ -3373,16 +1756,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
return -ENOMEM;
sroute->scomp = scomp;
-
- connect = kzalloc(sizeof(*connect), GFP_KERNEL);
- if (!connect) {
- kfree(sroute);
- return -ENOMEM;
- }
-
- connect->hdr.size = sizeof(*connect);
- connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT;
-
dev_dbg(scomp->dev, "sink %s control %s source %s\n",
route->sink, route->control ? route->control : "none",
route->source);
@@ -3406,8 +1779,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
source_swidget->id == snd_soc_dapm_output)
goto err;
- connect->source_id = source_swidget->comp_id;
-
/* sink component */
sink_swidget = snd_sof_find_swidget(scomp, (char *)route->sink);
if (!sink_swidget) {
@@ -3425,61 +1796,20 @@ static int sof_route_load(struct snd_soc_component *scomp, int index,
sink_swidget->id == snd_soc_dapm_output)
goto err;
- connect->sink_id = sink_swidget->comp_id;
-
- /*
- * For virtual routes, both sink and source are not
- * buffer. Since only buffer linked to component is supported by
- * FW, others are reported as error, add check in route function,
- * do not send it to FW when both source and sink are not buffer
- */
- if (source_swidget->id != snd_soc_dapm_buffer &&
- sink_swidget->id != snd_soc_dapm_buffer) {
- dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n",
- route->source, route->sink);
- goto err;
- } else {
- sroute->route = route;
- dobj->private = sroute;
- sroute->private = connect;
- sroute->src_widget = source_swidget;
- sroute->sink_widget = sink_swidget;
+ sroute->route = route;
+ dobj->private = sroute;
+ sroute->src_widget = source_swidget;
+ sroute->sink_widget = sink_swidget;
- /* add route to route list */
- list_add(&sroute->list, &sdev->route_list);
-
- return 0;
- }
+ /* add route to route list */
+ list_add(&sroute->list, &sdev->route_list);
+ return 0;
err:
- kfree(connect);
kfree(sroute);
return ret;
}
-int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
- struct snd_sof_widget *swidget)
-{
- struct sof_ipc_pipe_ready ready;
- struct sof_ipc_reply reply;
- int ret;
-
- dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
- swidget->widget->name, swidget->comp_id);
-
- memset(&ready, 0, sizeof(ready));
- ready.hdr.size = sizeof(ready);
- ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
- ready.comp_id = swidget->comp_id;
-
- ret = sof_ipc_tx_message(sdev->ipc,
- ready.hdr.cmd, &ready, sizeof(ready), &reply,
- sizeof(reply));
- if (ret < 0)
- return ret;
- return 1;
-}
-
/**
* sof_set_pipe_widget - Set pipe_widget for a component
* @sdev: pointer to struct snd_sof_dev
@@ -3519,8 +1849,38 @@ static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
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;
+ struct snd_sof_control *scontrol;
int ret;
+ /* first update all control IPC structures based on the IPC version */
+ if (ipc_tplg_ops->control_setup)
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+ ret = ipc_tplg_ops->control_setup(sdev, scontrol);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed updating IPC struct for control %s\n",
+ scontrol->name);
+ return ret;
+ }
+ }
+
+ /*
+ * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the
+ * topology will be removed and all widgets will be unloaded resulting in freeing all
+ * associated memories.
+ */
+ list_for_each_entry(swidget, &sdev->widget_list, list) {
+ if (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",
+ swidget->widget->name);
+ return ret;
+ }
+ }
+ }
+
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
list_for_each_entry(swidget, &sdev->widget_list, list) {
switch (swidget->id) {
@@ -3543,21 +1903,28 @@ static int sof_complete(struct snd_soc_component *scomp)
/* verify topology components loading including dynamic pipelines */
if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) {
- ret = sof_set_up_pipelines(sdev, true);
- if (ret < 0) {
- dev_err(sdev->dev, "error: topology verification failed %d\n", ret);
- return ret;
- }
+ 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 (ret < 0) {
+ dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n",
+ ret);
+ return ret;
+ }
- ret = sof_tear_down_pipelines(sdev, true);
- if (ret < 0) {
- dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret);
- return ret;
+ ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n",
+ ret);
+ return ret;
+ }
}
}
/* set up static pipelines */
- return sof_set_up_pipelines(sdev, false);
+ if (ipc_tplg_ops->set_up_all_pipelines)
+ return ipc_tplg_ops->set_up_all_pipelines(sdev, false);
+
+ return 0;
}
/* manifest - optional to inform component of manifest */
@@ -3641,6 +2008,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
/* DAI link - used for any driver specific init */
.link_load = sof_link_load,
+ .link_unload = sof_link_unload,
/* completion - called at completion of firmware loading */
.complete = sof_complete,
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index f13024c8ebf2..ea8e4506d02e 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -12,6 +12,7 @@
#include <linux/sched/signal.h>
#include "sof-priv.h"
#include "ops.h"
+#include "sof-utils.h"
#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
@@ -308,9 +309,6 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
lpos_64 = lpos;
lpos = do_div(lpos_64, buffer_size);
- if (count > buffer_size - lpos) /* min() not used to avoid sparse warnings */
- count = buffer_size - lpos;
-
/* get available count based on current host offset */
avail = sof_wait_trace_avail(sdev, lpos, buffer_size);
if (sdev->dtrace_error) {
@@ -319,8 +317,16 @@ static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer,
}
/* make sure count is <= avail */
- count = avail > count ? count : avail;
+ if (count > avail)
+ count = avail;
+ /*
+ * make sure that all trace data is available for the CPU as the trace
+ * data buffer might be allocated from non consistent memory.
+ * Note: snd_dma_buffer_sync() is called for normal audio playback and
+ * capture streams also.
+ */
+ snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU);
/* copy available trace data to debugfs */
rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
if (rem)
@@ -411,7 +417,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev)
sdev->host_offset = 0;
sdev->dtrace_draining = false;
- ret = snd_sof_dma_trace_init(sdev, &params.stream_tag);
+ ret = snd_sof_dma_trace_init(sdev, &params);
if (ret < 0) {
dev_err(sdev->dev,
"error: fail in snd_sof_dma_trace_init %d\n", ret);
@@ -465,8 +471,9 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev)
}
/* allocate trace data buffer */
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
- DMA_BUF_SIZE_FOR_TRACE, &sdev->dmatb);
+ ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
+ DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
+ &sdev->dmatb);
if (ret < 0) {
dev_err(sdev->dev,
"error: can't alloc buffer for trace %d\n", ret);