summaryrefslogtreecommitdiff
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/amd/acp-common.c3
-rw-r--r--sound/soc/sof/amd/acp-ipc.c4
-rw-r--r--sound/soc/sof/amd/acp.c30
-rw-r--r--sound/soc/sof/amd/acp.h2
-rw-r--r--sound/soc/sof/intel/Kconfig6
-rw-r--r--sound/soc/sof/intel/hda-mlink.c11
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c31
-rw-r--r--sound/soc/sof/intel/hda.c14
-rw-r--r--sound/soc/sof/intel/lnl.c40
-rw-r--r--sound/soc/sof/ipc3-control.c79
-rw-r--r--sound/soc/sof/ipc3-topology.c10
-rw-r--r--sound/soc/sof/ipc4-control.c124
-rw-r--r--sound/soc/sof/ipc4-pcm.c25
-rw-r--r--sound/soc/sof/ipc4-topology.c184
-rw-r--r--sound/soc/sof/nocodec.c4
-rw-r--r--sound/soc/sof/sof-audio.c9
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c1
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c1
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c1
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c23
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c11
-rw-r--r--sound/soc/sof/topology.c71
22 files changed, 483 insertions, 201 deletions
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index 0c3a92f5f942..df656cdc1527 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -149,7 +149,8 @@ static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *
break;
}
if (i == acp_data->info.count || !link->num_adr)
- break;
+ if (!mach->machine_check || mach->machine_check(acp_data->sdw))
+ break;
}
if (mach && mach->link_mask) {
mach->mach_params.subsystem_rev = acp_data->pci_rev;
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 3cd4674dd800..94025bc799ea 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -181,14 +181,14 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
}
dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
- if (dsp_msg) {
+ if (dsp_msg == ACP_DSP_MSG_SET) {
snd_sof_ipc_msgs_rx(sdev);
acp_dsp_ipc_host_done(sdev);
ipc_irq = true;
}
dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
- if (dsp_ack) {
+ if (dsp_ack == ACP_DSP_ACK_SET) {
if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
guard(spinlock_irq)(&sdev->ipc_lock);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 71a18f156de2..e6af8927baa0 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -223,7 +223,7 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd)
{
struct snd_sof_dev *sdev = adata->dev;
int ret;
- u32 data;
+ int data;
if (!cmd)
return -EINVAL;
@@ -377,6 +377,33 @@ void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src,
snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]);
}
+static int acp_init_scratch_mem_ipc_flags(struct snd_sof_dev *sdev)
+{
+ u32 dsp_msg_write, dsp_ack_write, host_msg_write, host_ack_write;
+
+ dsp_msg_write = sdev->debug_box.offset +
+ offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
+ dsp_ack_write = sdev->debug_box.offset +
+ offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
+ host_msg_write = sdev->debug_box.offset +
+ offsetof(struct scratch_ipc_conf, sof_host_msg_write);
+ host_ack_write = sdev->debug_box.offset +
+ offsetof(struct scratch_ipc_conf, sof_host_ack_write);
+ /* Initialize host message write flag */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg_write, 0);
+
+ /* Initialize host ack write flag */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_ack_write, 0);
+
+ /* Initialize DSP message write flag */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write, 0);
+
+ /* Initialize DSP ack write flag */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write, 0);
+
+ return 0;
+}
+
static int acp_memory_init(struct snd_sof_dev *sdev)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
@@ -384,6 +411,7 @@ static int acp_memory_init(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_CNTL_OFFSET,
ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
+ acp_init_scratch_mem_ipc_flags(sdev);
init_dma_descriptor(adata);
return 0;
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 2b7ea8c64106..7bcb76676a98 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -116,6 +116,8 @@
#define ACP_SRAM_PAGE_COUNT 128
#define ACP6X_SDW_MAX_MANAGER_COUNT 2
#define ACP70_SDW_MAX_MANAGER_COUNT ACP6X_SDW_MAX_MANAGER_COUNT
+#define ACP_DSP_MSG_SET 1
+#define ACP_DSP_ACK_SET 1
enum clock_source {
ACP_CLOCK_96M = 0,
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index e31f4c4061d8..915abbef398d 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -266,10 +266,8 @@ config SND_SOC_SOF_METEORLAKE
config SND_SOC_SOF_INTEL_LNL
tristate
- select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n
select SND_SOC_SOF_HDA_GENERIC
select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
- select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_SOUNDWIRE != n
select SND_SOC_SOF_IPC4
select SND_SOC_SOF_INTEL_MTL
@@ -329,8 +327,10 @@ config SND_SOC_SOF_HDA_GENERIC
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
select SND_SOC_SOF_HDA_PROBES
- select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE
+ select SND_SOC_SDW_UTILS if SND_SOC_SOF_INTEL_SOUNDWIRE !=n
select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
+ select SND_SOF_SOF_HDA_SDW_BPT if SND_SOC_SOF_INTEL_LNL != n && \
+ SND_SOC_SOF_INTEL_SOUNDWIRE !=n
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index ce603a2343de..92314e3b568a 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -988,24 +988,19 @@ struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
}
EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, "SND_SOC_SOF_HDA_MLINK");
-int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
+void hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
{
struct hdac_ext2_link *h2link;
struct hdac_ext_link *hlink;
h2link = find_ext2_link(bus, alt, elid);
- if (!h2link)
- return -ENODEV;
-
- if (!h2link->ofls)
- return 0;
+ if (!h2link || !h2link->ofls)
+ return;
hlink = &h2link->hext_link;
scoped_guard(mutex, &h2link->eml_lock)
hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
-
- return 0;
}
EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/intel/hda-sdw-bpt.c b/sound/soc/sof/intel/hda-sdw-bpt.c
index 728ffe7ae54d..7351a0870725 100644
--- a/sound/soc/sof/intel/hda-sdw-bpt.c
+++ b/sound/soc/sof/intel/hda-sdw-bpt.c
@@ -322,7 +322,8 @@ int hda_sdw_bpt_open(struct device *dev, int link_id, struct hdac_ext_stream **b
__func__, ret);
close:
- ret1 = hda_sdw_bpt_close(dev, *bpt_tx_stream, dmab_tx_bdl, *bpt_rx_stream, dmab_rx_bdl);
+ ret1 = hda_sdw_bpt_close(dev, link_id, *bpt_tx_stream, dmab_tx_bdl,
+ *bpt_rx_stream, dmab_rx_bdl);
if (ret1 < 0)
dev_err(dev, "%s: hda_sdw_bpt_close failed: %d\n",
__func__, ret1);
@@ -447,14 +448,38 @@ dma_disable:
}
EXPORT_SYMBOL_NS(hda_sdw_bpt_wait, "SND_SOC_SOF_INTEL_HDA_SDW_BPT");
-int hda_sdw_bpt_close(struct device *dev, struct hdac_ext_stream *bpt_tx_stream,
+int hda_sdw_bpt_close(struct device *dev, int link_id, struct hdac_ext_stream *bpt_tx_stream,
struct snd_dma_buffer *dmab_tx_bdl, struct hdac_ext_stream *bpt_rx_stream,
struct snd_dma_buffer *dmab_rx_bdl)
{
+ struct snd_sof_dev *sdev = dev_get_drvdata(dev);
int ret;
int ret1;
- ret = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl);
+ /*
+ * In the case of SoundWire we need to reset the PCMSyCM registers.
+ * Need to continue depreparing the DMA buffers even if this fails.
+ */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id,
+ 0, /* PDI0 */
+ 0, 0, SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret < 0)
+ dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d for PDI0\n",
+ __func__, ret);
+
+ ret1 = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id,
+ 1, /* PDI1 */
+ 0, 0, SNDRV_PCM_STREAM_CAPTURE);
+ if (ret1 < 0) {
+ dev_err(dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d for PDI1\n",
+ __func__, ret1);
+ if (!ret)
+ ret = ret1;
+ }
+
+ ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_rx_stream, dmab_rx_bdl);
+ if (!ret)
+ ret = ret1;
ret1 = hda_sdw_bpt_dma_deprepare(dev, bpt_tx_stream, dmab_tx_bdl);
if (!ret)
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 8662b422eb80..dc85903b8d46 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -1159,7 +1159,6 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev,
struct snd_soc_acpi_adr_device *adr_dev;
const char *name_prefix = "";
int index = link->num_adr;
- bool is_amp = true; /* Set it to false if the codec wiah any NON-AMP DAI type */
int ep_index = 0;
int i, j;
@@ -1216,7 +1215,6 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev,
endpoints[ep_index].aggregated = 0;
endpoints[ep_index].group_id = 0;
endpoints[ep_index].group_position = 0;
- is_amp = false;
}
ep_index++;
}
@@ -1230,16 +1228,6 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev,
return NULL;
}
- /*
- * codec_info_list[].is_amp is a codec-level override: for multi-function
- * codecs we must treat the whole codec as an AMP when it is described as
- * such in the codec info table, even if some endpoints were detected as
- * non-AMP above. Callers/UCM rely on this to keep name_prefix and AMP
- * indexing stable and backwards compatible.
- */
- if (codec_info_list[i].is_amp)
- is_amp = true;
-
adr_dev[index].adr = ((u64)sdw_device->id.class_id & 0xFF) |
((u64)sdw_device->id.part_id & 0xFFFF) << 8 |
((u64)sdw_device->id.mfg_id & 0xFFFF) << 24 |
@@ -1247,7 +1235,7 @@ static struct snd_soc_acpi_adr_device *find_acpi_adr_device(struct device *dev,
((u64)(sdw_device->id.sdw_version & 0xF) << 44) |
((u64)(sdw_device->bus->link_id & 0xF) << 48);
- if (!is_amp) {
+ if (!codec_info_list[i].is_amp) {
/* For non-amp codecs, get name_prefix from codec_info_list[] */
adr_dev[index].name_prefix = devm_kasprintf(dev, GFP_KERNEL, "%s", name_prefix);
goto done_name_prefix;
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
index c01ea7e731aa..83703ebc6385 100644
--- a/sound/soc/sof/intel/lnl.c
+++ b/sound/soc/sof/intel/lnl.c
@@ -20,22 +20,12 @@
#include "lnl.h"
#include <sound/hda-mlink.h>
-/* this helps allows the DSP to setup DMIC/SSP */
-static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable)
+/* Configure DSP offload for DMIC/SSP/UAOL */
+static void hdac_bus_set_dsp_offload(struct hdac_bus *bus, bool enable)
{
- int ret;
-
- ret = hdac_bus_eml_enable_offload(bus, true,
- AZX_REG_ML_LEPTR_ID_INTEL_SSP, enable);
- if (ret < 0)
- return ret;
-
- ret = hdac_bus_eml_enable_offload(bus, true,
- AZX_REG_ML_LEPTR_ID_INTEL_DMIC, enable);
- if (ret < 0)
- return ret;
-
- return 0;
+ hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP, enable);
+ hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC, enable);
+ hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_UAOL, enable);
}
static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev)
@@ -46,18 +36,14 @@ static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+ hdac_bus_set_dsp_offload(sof_to_bus(sdev), true);
+
+ return 0;
}
static void lnl_hda_dsp_remove(struct snd_sof_dev *sdev)
{
- int ret;
-
- ret = hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), false);
- if (ret < 0)
- dev_warn(sdev->dev,
- "Failed to disable offload for DMIC/SSP: %d\n", ret);
-
+ hdac_bus_set_dsp_offload(sof_to_bus(sdev), false);
hda_dsp_remove(sdev);
}
@@ -69,7 +55,9 @@ static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+ hdac_bus_set_dsp_offload(sof_to_bus(sdev), true);
+
+ return 0;
}
static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
@@ -80,7 +68,9 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+ hdac_bus_set_dsp_offload(sof_to_bus(sdev), true);
+
+ return 0;
}
static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index 2b1befad6d5c..d1697401b1da 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > scontrol->max_size - sizeof(*data)) {
+ if (data->size > scontrol->max_size - sizeof(*cdata) -
+ 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));
+ data->size,
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(*data));
return -EINVAL;
}
@@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_abi_hdr *data = cdata->data;
+ const struct sof_abi_hdr *new_hdr =
+ (const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
@@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
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));
+ /* Validate the new data's size, not the old one */
+ if (new_hdr->size > scontrol->max_size - sizeof(*cdata) -
+ sizeof(*new_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "data size too big %u bytes max is %zu\n",
+ new_hdr->size,
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(*new_hdr));
return -EINVAL;
}
- size = data->size + sizeof(*data);
+ size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
@@ -389,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* 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);
+ if (header.length > scontrol->max_size - sizeof(*cdata)) {
+ dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n",
+ header.length, scontrol->max_size - sizeof(*cdata));
+ return -EINVAL;
+ }
+
+ /* Ensure the data is large enough to contain the ABI header */
+ if (header.length < sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "Bytes data size %u less than ABI header %zu\n",
+ header.length, sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -427,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
+ if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
goto err_restore;
}
@@ -443,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
err_restore:
/* If we have an issue, we restore the old, valid bytes control data */
if (scontrol->old_ipc_control_data) {
- memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size);
+ memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
}
@@ -482,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
}
/* 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",
+ if (cdata->data->size > scontrol->max_size - sizeof(*cdata) -
+ sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "User data size %u exceeds max size %zu\n",
cdata->data->size,
- scontrol->max_size - sizeof(struct sof_abi_hdr));
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -535,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol,
return;
}
+ /* Verify the size fits within the allocation */
+ if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) -
+ sizeof(*local_cdata->data)) {
+ dev_err(scomp->dev,
+ "cdata binary size %u exceeds buffer\n",
+ cdata->num_elems);
+ return;
+ }
+
/* copy the new binary data */
memcpy(local_cdata->data, cdata->data, cdata->num_elems);
} else if (cdata->num_elems != scontrol->num_channels) {
@@ -626,16 +655,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_
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);
+ if (check_mul_overflow((size_t)cdata->num_elems,
+ sizeof(struct sof_ipc_ctrl_value_chan),
+ &expected_size))
+ return;
+ if (check_add_overflow(expected_size,
+ sizeof(struct sof_ipc_ctrl_data),
+ &expected_size))
+ return;
break;
case SOF_CTRL_TYPE_DATA_GET:
case SOF_CTRL_TYPE_DATA_SET:
- expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
+ if (check_add_overflow((size_t)cdata->num_elems,
+ sizeof(struct sof_abi_hdr),
+ &expected_size))
+ return;
+ if (check_add_overflow(expected_size,
+ sizeof(struct sof_ipc_ctrl_data),
+ &expected_size))
+ return;
break;
default:
return;
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 8006777f2f64..4e066bbded91 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -519,6 +519,7 @@ static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
static int sof_ipc3_widget_setup_comp_pipeline(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_pipeline *spipe = swidget->spipe;
struct sof_ipc_pipe_new *pipeline;
struct snd_sof_widget *comp_swidget;
@@ -559,8 +560,15 @@ static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
if (ret < 0)
goto err;
- if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))
+ if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) {
+ pipeline->core = SOF_DSP_PRIMARY_CORE;
+ } else if (pipeline->core > sdev->num_cores - 1) {
+ dev_info(scomp->dev,
+ "out of range core id for %s, moving it %d -> %d\n",
+ swidget->widget->name, pipeline->core,
+ SOF_DSP_PRIMARY_CORE);
pipeline->core = SOF_DSP_PRIMARY_CORE;
+ }
if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))
swidget->dynamic_pipeline_widget =
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 596c3d77a34e..8d86d32a16ca 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -13,13 +13,12 @@
#include "ipc4-topology.h"
static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ struct sof_ipc4_msg *msg,
bool set, bool lock)
{
- struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_ops *iops = sdev->ipc->ops;
- struct sof_ipc4_msg *msg = &cdata->msg;
struct snd_sof_widget *swidget;
bool widget_found = false;
int ret = 0;
@@ -88,9 +87,9 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_gain *gain = swidget->private;
- struct sof_ipc4_msg *msg = &cdata->msg;
struct sof_ipc4_gain_params params;
bool all_channels_equal = true;
+ struct sof_ipc4_msg msg;
u32 value;
int ret, i;
@@ -107,6 +106,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
* notify DSP with a single IPC message if all channel values are equal. Otherwise send
* a separate IPC for each channel.
*/
+ memcpy(&msg, &cdata->msg, sizeof(msg));
for (i = 0; i < scontrol->num_channels; i++) {
if (all_channels_equal) {
params.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
@@ -121,12 +121,10 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
params.curve_duration_h = gain->data.params.curve_duration_h;
params.curve_type = gain->data.params.curve_type;
- msg->data_ptr = &params;
- msg->data_size = sizeof(params);
+ msg.data_ptr = &params;
+ msg.data_size = sizeof(params);
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
- msg->data_ptr = NULL;
- msg->data_size = 0;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock);
if (ret < 0) {
dev_err(sdev->dev, "Failed to set volume update for %s\n",
scontrol->name);
@@ -208,7 +206,7 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_control_msg_payload *data;
- struct sof_ipc4_msg *msg = &cdata->msg;
+ struct sof_ipc4_msg msg;
size_t data_size;
unsigned int i;
int ret;
@@ -225,12 +223,11 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,
data->chanv[i].value = cdata->chanv[i].value;
}
- msg->data_ptr = data;
- msg->data_size = data_size;
+ memcpy(&msg, &cdata->msg, sizeof(msg));
+ msg.data_ptr = data;
+ msg.data_size = data_size;
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
- msg->data_ptr = NULL;
- msg->data_size = 0;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock);
if (ret < 0)
dev_err(sdev->dev, "Failed to set control update for %s\n",
scontrol->name);
@@ -245,7 +242,7 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_ipc4_control_msg_payload *data;
- struct sof_ipc4_msg *msg = &cdata->msg;
+ struct sof_ipc4_msg msg;
size_t data_size;
unsigned int i;
int ret;
@@ -263,13 +260,13 @@ static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)
data->id = cdata->index;
data->num_elems = scontrol->num_channels;
- msg->data_ptr = data;
- msg->data_size = data_size;
+
+ memcpy(&msg, &cdata->msg, sizeof(msg));
+ msg.data_ptr = data;
+ msg.data_size = data_size;
scontrol->comp_data_dirty = false;
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, true);
- msg->data_ptr = NULL;
- msg->data_size = 0;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, false, true);
if (!ret) {
for (i = 0; i < scontrol->num_channels; i++) {
cdata->chanv[i].channel = data->chanv[i].channel;
@@ -291,7 +288,7 @@ sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_ipc4_control_msg_payload *msg_data;
struct sof_abi_hdr *data = cdata->data;
- struct sof_ipc4_msg *msg = &cdata->msg;
+ struct sof_ipc4_msg msg;
size_t data_size;
int ret;
@@ -304,14 +301,13 @@ sof_ipc4_set_bytes_control_data(struct snd_sof_control *scontrol, bool lock)
msg_data->num_elems = data->size;
memcpy(msg_data->data, data->data, data->size);
- msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+ memcpy(&msg, &cdata->msg, sizeof(msg));
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
- msg->data_ptr = msg_data;
- msg->data_size = data_size;
+ msg.data_ptr = msg_data;
+ msg.data_size = data_size;
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
- msg->data_ptr = NULL;
- msg->data_size = 0;
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock);
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to set control update for %s\n",
__func__, scontrol->name);
@@ -328,7 +324,7 @@ sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_ipc4_control_msg_payload *msg_data;
struct sof_abi_hdr *data = cdata->data;
- struct sof_ipc4_msg *msg = &cdata->msg;
+ struct sof_ipc4_msg msg;
size_t data_size;
int ret = 0;
@@ -346,29 +342,30 @@ sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
if (!msg_data)
return -ENOMEM;
- msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+ memcpy(&msg, &cdata->msg, sizeof(msg));
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
msg_data->id = cdata->index;
msg_data->num_elems = 0; /* ignored for bytes */
- msg->data_ptr = msg_data;
- msg->data_size = data_size;
+ msg.data_ptr = msg_data;
+ msg.data_size = data_size;
scontrol->comp_data_dirty = false;
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, lock);
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, false, lock);
if (!ret) {
- if (msg->data_size > scontrol->max_size - sizeof(*data)) {
+ if (msg.data_size > scontrol->max_size - sizeof(*data)) {
dev_err(scomp->dev,
"%s: no space for data in %s (%zu, %zu)\n",
- __func__, scontrol->name, msg->data_size,
+ __func__, scontrol->name, msg.data_size,
scontrol->max_size - sizeof(*data));
ret = -EINVAL;
goto out;
}
- data->size = msg->data_size;
+ data->size = msg.data_size;
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
- memcpy(data->data, msg->data_ptr, data->size);
+ memcpy(data->data, msg.data_ptr, data->size);
} else {
dev_err(scomp->dev, "Failed to read control data for %s\n",
scontrol->name);
@@ -376,9 +373,6 @@ sof_ipc4_refresh_bytes_control(struct snd_sof_control *scontrol, bool lock)
}
out:
- msg->data_ptr = NULL;
- msg->data_size = 0;
-
kfree(msg_data);
return ret;
@@ -508,7 +502,7 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_abi_hdr *data = cdata->data;
- struct sof_ipc4_msg *msg = &cdata->msg;
+ struct sof_ipc4_msg msg;
int ret = 0;
/* Send the new data to the firmware only if it is powered up */
@@ -530,28 +524,26 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
return sof_ipc4_refresh_bytes_control(scontrol, lock);
}
- msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+ memcpy(&msg, &cdata->msg, sizeof(msg));
+ msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
- msg->data_ptr = data->data;
+ msg.data_ptr = data->data;
if (set)
- msg->data_size = data->size;
+ msg.data_size = data->size;
else
- msg->data_size = scontrol->max_size - sizeof(*data);
+ msg.data_size = scontrol->max_size - sizeof(*data);
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, set, lock);
if (ret < 0) {
dev_err(sdev->dev, "Failed to %s for %s\n",
set ? "set bytes update" : "get bytes",
scontrol->name);
} else if (!set) {
/* Update the sizes according to the received payload data */
- data->size = msg->data_size;
+ data->size = msg.data_size;
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
}
- msg->data_ptr = NULL;
- msg->data_size = 0;
-
return ret;
}
@@ -562,6 +554,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
+ const struct sof_abi_hdr *new_hdr =
+ (const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
int ret;
@@ -572,15 +566,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
- /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > scontrol->max_size - sizeof(*data)) {
+ /* Validate the new data's size, not the old one */
+ if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) {
dev_err_ratelimited(scomp->dev,
"data size too big %u bytes max is %zu\n",
- data->size, scontrol->max_size - sizeof(*data));
+ new_hdr->size,
+ scontrol->max_size - sizeof(*new_hdr));
return -EINVAL;
}
- size = data->size + sizeof(*data);
+ size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
@@ -880,6 +875,16 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
*/
if (type == SND_SOC_TPLG_TYPE_BYTES) {
struct sof_abi_hdr *data = cdata->data;
+ size_t source_size = struct_size(msg_data, data, msg_data->num_elems);
+
+ if (source_size > ndata->event_data_size) {
+ dev_warn(sdev->dev,
+ "%s: invalid bytes notification size for %s (%zu, %u)\n",
+ __func__, scontrol->name, source_size,
+ ndata->event_data_size);
+ scontrol->comp_data_dirty = true;
+ goto notify;
+ }
if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) {
dev_warn(sdev->dev,
@@ -892,6 +897,17 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
scontrol->size = sizeof(*cdata) + sizeof(*data) + data->size;
}
} else {
+ size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems);
+
+ if (source_size > ndata->event_data_size) {
+ dev_warn(sdev->dev,
+ "%s: invalid channel notification size for %s (%zu, %u)\n",
+ __func__, scontrol->name, source_size,
+ ndata->event_data_size);
+ scontrol->comp_data_dirty = true;
+ goto notify;
+ }
+
for (i = 0; i < msg_data->num_elems; i++) {
u32 channel = msg_data->chanv[i].channel;
@@ -919,6 +935,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)
scontrol->comp_data_dirty = true;
}
+notify:
+
/*
* Look up the ALSA kcontrol of the scontrol to be able to send a
* notification to user space
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index fc3ead77e5ea..5929ecf6642e 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -528,7 +528,19 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
if (ret < 0) {
spcm_err(spcm, substream->stream, "failed to pause all pipelines\n");
- goto free;
+ /*
+ * workaround: if the firmware is crashed or the IPC timed out
+ * while setting the pipeline state we must ignore the error
+ * code and proceed to set adjust the local pipeline states.
+ *
+ * If the firmware is crashed we will not send IPC messages
+ * and we are going to see errors printed, but the state of the
+ * widgets will be correct for the next boot.
+ */
+ if (sdev->fw_state != SOF_FW_CRASHED && ret != -ETIMEDOUT)
+ goto free;
+
+ ret = 0;
}
/* update PAUSED state for all pipelines just triggered */
@@ -560,14 +572,15 @@ skip_pause_transition:
"failed to set final state %d for all pipelines\n",
state);
/*
- * workaround: if the firmware is crashed while setting the
- * pipelines to reset state we must ignore the error code and
- * reset it to 0.
- * Since the firmware is crashed we will not send IPC messages
+ * workaround: if the firmware is crashed or the IPC timed out
+ * while setting the pipeline state we must ignore the error
+ * code and proceed to set adjust the local pipeline states.
+ *
+ * If the firmware is crashed we will not send IPC messages
* and we are going to see errors printed, but the state of the
* widgets will be correct for the next boot.
*/
- if (sdev->fw_state != SOF_FW_CRASHED || state != SOF_IPC4_PIPE_RESET)
+ if (sdev->fw_state != SOF_FW_CRASHED && ret != -ETIMEDOUT)
goto free;
ret = 0;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 76812d8fb567..6fdfb667cce8 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -238,6 +238,23 @@ struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev,
return NULL;
}
+static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg)
+{
+ /* Fetch the sample type from the fmt for 8 and 32 bit formats */
+ u32 __bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt_cfg);
+
+ if (__bits == 8 || __bits == 32)
+ return SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(fmt_cfg);
+
+ /*
+ * Return LSB integer type for 16, 20 and 24 formats as the firmware is
+ * handling the LSB/MSB alignment internally, for the kernel this
+ * should not be taken into account, we treat them as LSB to match with
+ * the format we support on the PCM side.
+ */
+ return SOF_IPC4_TYPE_LSB_INTEGER;
+}
+
static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,
int num_formats)
{
@@ -246,8 +263,9 @@ static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_fo
for (i = 0; i < num_formats; i++) {
struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;
dev_dbg(dev,
- "Pin #%d: %uHz, %ubit, %luch (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
+ "Pin #%d: %uHz, %ubit (type: %u), %luch (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth,
+ sof_ipc4_fmt_cfg_to_type(fmt->fmt_cfg),
SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg),
fmt->ch_map, fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
pin_fmt[i].buffer_size);
@@ -624,6 +642,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
struct sof_ipc4_copier *ipc4_copier;
+ struct snd_sof_pcm_stream *sps;
struct snd_sof_pcm *spcm;
int node_type = 0;
int ret, dir;
@@ -668,24 +687,23 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
if (ret)
goto free_available_fmt;
- if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
- struct snd_sof_pcm_stream *sps = &spcm->stream[dir];
+ sps = &spcm->stream[dir];
+ sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms,
+ SOF_COPIER_DEEP_BUFFER_TOKENS,
+ swidget->tuples,
+ swidget->num_tuples, sizeof(u32), 1);
- sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms,
- SOF_COPIER_DEEP_BUFFER_TOKENS,
- swidget->tuples,
- swidget->num_tuples, sizeof(u32), 1);
- /* Set default DMA buffer size if it is not specified in topology */
- if (!sps->dsp_max_burst_size_in_ms) {
- struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ /* Set default DMA buffer size if it is not specified in topology */
+ if (!sps->dsp_max_burst_size_in_ms) {
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
sps->dsp_max_burst_size_in_ms = pipeline->use_chain_dma ?
SOF_IPC4_CHAIN_DMA_BUFFER_SIZE : SOF_IPC4_MIN_DMA_BUFFER_SIZE;
- }
- } else {
- /* Capture data is copied from DSP to host in 1ms bursts */
- spcm->stream[dir].dsp_max_burst_size_in_ms = 1;
+ else
+ /* Capture data is copied from DSP to host in 1ms bursts */
+ sps->dsp_max_burst_size_in_ms = 1;
}
skip_gtw_cfg:
@@ -927,6 +945,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
static int sof_ipc4_widget_setup_comp_pipeline(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_ipc4_pipeline *pipeline;
struct snd_sof_pipeline *spipe = swidget->spipe;
int ret;
@@ -942,6 +961,16 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
goto err;
}
+ if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) {
+ pipeline->core_id = SOF_DSP_PRIMARY_CORE;
+ } else if (pipeline->core_id > sdev->num_cores - 1) {
+ dev_info(scomp->dev,
+ "out of range core id for %s, moving it %d -> %d\n",
+ swidget->widget->name, pipeline->core_id,
+ SOF_DSP_PRIMARY_CORE);
+ pipeline->core_id = SOF_DSP_PRIMARY_CORE;
+ }
+
swidget->core = pipeline->core_id;
spipe->core_mask |= BIT(pipeline->core_id);
if (pipeline->direction_valid) {
@@ -1092,6 +1121,15 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)
if (ret)
goto err;
+ if (!src->available_fmt.num_input_formats ||
+ !src->available_fmt.num_output_formats) {
+ dev_err(scomp->dev,
+ "Invalid number of formats: input: %d, output: %d\n",
+ src->available_fmt.num_input_formats,
+ src->available_fmt.num_output_formats);
+ goto err;
+ }
+
ret = sof_update_ipc_object(scomp, &src->data, SOF_SRC_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*src), 1);
if (ret) {
@@ -1135,6 +1173,15 @@ static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget)
if (ret)
goto err;
+ if (!asrc->available_fmt.num_input_formats ||
+ !asrc->available_fmt.num_output_formats) {
+ dev_err(scomp->dev,
+ "Invalid number of formats: input: %d, output: %d\n",
+ asrc->available_fmt.num_input_formats,
+ asrc->available_fmt.num_output_formats);
+ goto err;
+ }
+
ret = sof_update_ipc_object(scomp, &asrc->data, SOF_ASRC_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*asrc), 1);
if (ret) {
@@ -1353,23 +1400,6 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
return 0;
}
-static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg)
-{
- /* Fetch the sample type from the fmt for 8 and 32 bit formats */
- u32 __bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt_cfg);
-
- if (__bits == 8 || __bits == 32)
- return SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(fmt_cfg);
-
- /*
- * Return LSB integer type for 20 and 24 formats as the firmware is
- * handling the LSB/MSB alignment internally, for the kernel this
- * should not be taken into account, we treat them as LSB to match with
- * the format we support on the PCM side.
- */
- return SOF_IPC4_TYPE_LSB_INTEGER;
-}
-
/* update hw_params based on the audio stream format */
static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
struct sof_ipc4_audio_format *fmt, u32 param_to_update)
@@ -2430,10 +2460,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
copier_data->gtw_cfg.dma_buffer_size);
break;
case snd_soc_dapm_dai_out:
- case snd_soc_dapm_aif_out:
copier_data->gtw_cfg.dma_buffer_size =
SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs;
break;
+ case snd_soc_dapm_aif_out:
+ copier_data->gtw_cfg.dma_buffer_size =
+ max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
+ copier_data->base_config.obs;
+ dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)",
+ swidget->widget->name,
+ deep_buffer_dma_ms ? " (using Deep Buffer)" : "",
+ max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms),
+ copier_data->gtw_cfg.dma_buffer_size);
+ break;
default:
break;
}
@@ -2611,16 +2650,6 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
return input_fmt_index;
/*
- * For playback, the SRC sink rate will be configured based on the requested output
- * format, which is restricted to only deal with DAI's with a single format for now.
- */
- if (dir == SNDRV_PCM_STREAM_PLAYBACK && available_fmt->num_output_formats > 1) {
- dev_err(sdev->dev, "Invalid number of output formats: %d for SRC %s\n",
- available_fmt->num_output_formats, swidget->widget->name);
- return -EINVAL;
- }
-
- /*
* SRC does not perform format conversion, so the output channels and valid bit depth must
* be the same as that of the input.
*/
@@ -2629,12 +2658,36 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg);
out_ref_type = sof_ipc4_fmt_cfg_to_type(in_audio_fmt->fmt_cfg);
- /*
- * For capture, the SRC module should convert the rate to match the rate requested by the
- * PCM hw_params. Set the reference params based on the fe_params unconditionally as it
- * will be ignored for playback anyway.
- */
- out_ref_rate = params_rate(fe_params);
+ if (src->data.sink_rate) {
+ /* Use the sink rate as reference */
+ out_ref_rate = src->data.sink_rate;
+ } else if (dir == SNDRV_PCM_STREAM_CAPTURE) {
+ /*
+ * Use the fe rate as reference for capture if the sink rate is
+ * not set since we need to convert to the rate the PCM device
+ * is openned with
+ */
+ out_ref_rate = params_rate(fe_params);
+ } else {
+ /*
+ * Otherwise try to guess what the rate should be:
+ * The output formats must have single rate specified if the
+ * sink rate is not set for an SRC in playback path.
+ */
+ int i;
+
+ out_audio_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
+ out_ref_rate = out_audio_fmt->sampling_frequency;
+ for (i = 1; i < available_fmt->num_output_formats; i++) {
+ out_audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
+ if (out_ref_rate != out_audio_fmt->sampling_frequency) {
+ dev_err(sdev->dev,
+ "Cannot determine the output rate for SRC: %s\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+ }
+ }
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
&src->data.base_config,
@@ -3151,6 +3204,15 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
ipc_size = ipc4_copier->ipc_config_size;
ipc_data = ipc4_copier->ipc_config_data;
+ /*
+ * Refresh copier_data in ipc_config_data for host copiers.
+ * The node_id may have been updated by host_config after
+ * ipc_prepare, e.g. when host stream tags change after a
+ * suspend/resume cycle.
+ */
+ if (swidget->id != snd_soc_dapm_buffer)
+ memcpy(ipc_data, &ipc4_copier->data, sizeof(ipc4_copier->data));
+
msg = &ipc4_copier->msg;
break;
}
@@ -3159,6 +3221,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
{
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier = dai->private;
+ struct sof_ipc4_copier_data *copier_data;
+ u32 gtw_cfg_config_length;
+ u32 tlv_size;
pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
@@ -3167,6 +3232,27 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
ipc_size = ipc4_copier->ipc_config_size;
ipc_data = ipc4_copier->ipc_config_data;
+ /*
+ * Refresh copier_data and dma_config_tlv in ipc_config_data.
+ * These may have been updated after ipc_prepare, e.g. when
+ * link DMA stream tags change after a suspend/resume cycle.
+ *
+ * copier_data->gtw_cfg.config_length does not include the
+ * TLV size (it was restored after sof_ipc4_prepare_copier_module),
+ * so temporarily inflate it to match the ipc_config_data layout.
+ */
+ copier_data = &ipc4_copier->data;
+ gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;
+ tlv_size = ipc_size - sizeof(*copier_data) - gtw_cfg_config_length;
+
+ copier_data->gtw_cfg.config_length += tlv_size / 4;
+ memcpy(ipc_data, copier_data, sizeof(*copier_data));
+ copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4;
+
+ if (tlv_size)
+ memcpy(ipc_data + sizeof(*copier_data) + gtw_cfg_config_length,
+ &ipc4_copier->dma_config_tlv, tlv_size);
+
msg = &ipc4_copier->msg;
break;
}
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index c0c906a78eba..11a95dba3c9c 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -15,7 +15,6 @@
static struct snd_soc_card sof_nocodec_card = {
.name = "nocodec", /* the sof- prefix is added by the core */
- .topology_shortname = "sof-nocodec",
.owner = THIS_MODULE
};
@@ -89,9 +88,10 @@ static int sof_nocodec_probe(struct platform_device *pdev)
int ret;
card->dev = &pdev->dev;
- card->topology_shortname_created = true;
mach = pdev->dev.platform_data;
+ snd_soc_card_set_topology_name(card, "sof");
+
ret = sof_nocodec_setup(card->dev, mach->mach_params.num_dai_drivers,
mach->mach_params.dai_drivers);
if (ret < 0)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index acf56607bc9c..24614e506019 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -146,7 +146,6 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
{
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_pipeline *spipe = swidget->spipe;
- bool use_count_decremented = false;
int ret;
int i;
@@ -225,9 +224,10 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
return 0;
widget_free:
- /* widget use_count will be decremented by sof_widget_free() */
+ /* widget use_count and core_put handled by sof_widget_free() */
sof_widget_free_unlocked(sdev, swidget);
- use_count_decremented = true;
+ return ret;
+
pipe_widget_free:
if (swidget->id != snd_soc_dapm_scheduler) {
sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
@@ -242,8 +242,7 @@ pipe_widget_free:
}
}
use_count_dec:
- if (!use_count_decremented)
- swidget->use_count--;
+ swidget->use_count--;
return ret;
}
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index 7b72d1c9c739..2396cc35489a 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -10,7 +10,6 @@
#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>
diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c
index d5984990098a..02d0d97ad1a0 100644
--- a/sound/soc/sof/sof-client-ipc-kernel-injector.c
+++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c
@@ -7,7 +7,6 @@
#include <linux/auxiliary_bus.h>
#include <linux/debugfs.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <sound/sof/header.h>
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
index c28f106de6ba..932ab459c079 100644
--- a/sound/soc/sof/sof-client-ipc-msg-injector.c
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -9,7 +9,6 @@
#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>
diff --git a/sound/soc/sof/sof-client-probes-ipc3.c b/sound/soc/sof/sof-client-probes-ipc3.c
index a78ec0954a61..a3e382d6161f 100644
--- a/sound/soc/sof/sof-client-probes-ipc3.c
+++ b/sound/soc/sof/sof-client-probes-ipc3.c
@@ -107,7 +107,7 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
struct device *dev = &cdev->auxdev.dev;
struct sof_ipc_probe_info_params msg = {{{0}}};
struct sof_ipc_probe_info_params *reply;
- size_t bytes;
+ size_t bytes, elem_size, payload_size;
int ret;
*params = NULL;
@@ -128,14 +128,29 @@ static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
if (ret < 0 || reply->rhdr.error < 0)
goto exit;
+ payload_size = reply->rhdr.hdr.size;
+ if (payload_size < offsetof(struct sof_ipc_probe_info_params, dma)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
if (!reply->num_elems)
goto exit;
if (cmd == SOF_IPC_PROBE_DMA_INFO)
- bytes = sizeof(reply->dma[0]);
+ elem_size = sizeof(reply->dma[0]);
else
- bytes = sizeof(reply->desc[0]);
- bytes *= reply->num_elems;
+ elem_size = sizeof(reply->desc[0]);
+
+ payload_size -= offsetof(struct sof_ipc_probe_info_params, dma);
+ if (reply->num_elems > payload_size / elem_size) {
+ dev_err(dev, "%s: invalid probe info element count %u\n",
+ __func__, reply->num_elems);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ bytes = reply->num_elems * elem_size;
*params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
if (!*params) {
ret = -ENOMEM;
diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c
index 88397c7dc4c3..2eef32b55395 100644
--- a/sound/soc/sof/sof-client-probes-ipc4.c
+++ b/sound/soc/sof/sof-client-probes-ipc4.c
@@ -248,10 +248,19 @@ static int ipc4_probes_points_info(struct sof_client_dev *cdev,
return ret;
}
info = msg.data_ptr;
+ if (msg.data_size < sizeof(*info) ||
+ info->num_elems > (msg.data_size - sizeof(*info)) /
+ sizeof(info->points[0])) {
+ dev_err(dev, "%s: invalid probe info element count %u\n",
+ __func__, info->num_elems);
+ kfree(msg.data_ptr);
+ return -EINVAL;
+ }
+
*num_desc = info->num_elems;
dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc);
- *desc = kzalloc(*num_desc * sizeof(**desc), GFP_KERNEL);
+ *desc = kcalloc(*num_desc, sizeof(**desc), GFP_KERNEL);
if (!*desc) {
kfree(msg.data_ptr);
return -ENOMEM;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 63d582c65891..42a2d90bb705 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -23,6 +23,13 @@ static bool disable_function_topology;
module_param(disable_function_topology, bool, 0444);
MODULE_PARM_DESC(disable_function_topology, "Disable function topology loading");
+#define MAX_FEATURE_TPLG_COUNT 16
+
+static char *feature_topologies[MAX_FEATURE_TPLG_COUNT];
+static int feature_tplg_cnt;
+module_param_array(feature_topologies, charp, &feature_tplg_cnt, 0444);
+MODULE_PARM_DESC(feature_topologies, "Topology list for virtual loop DAI link");
+
#define COMP_ID_UNASSIGNED 0xffffffff
/*
* Constants used in the computation of linear volume gain
@@ -733,10 +740,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
int ret;
while (array_size > 0 && total < count * token_instance_num) {
+ if (array_size < (int)sizeof(*array))
+ return -EINVAL;
+
asize = le32_to_cpu(array->size);
/* validate asize */
- if (asize < sizeof(*array)) {
+ if (asize < (int)sizeof(*array)) {
dev_err(scomp->dev, "error: invalid array size 0x%x\n",
asize);
return -EINVAL;
@@ -1567,8 +1577,15 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
int core = sof_get_token_value(SOF_TKN_COMP_CORE_ID, swidget->tuples,
swidget->num_tuples);
- if (core >= 0)
+ if (core >= 0) {
+ if (core > sdev->num_cores - 1) {
+ dev_info(scomp->dev,
+ "out of range core id for %s, moving it %d -> %d\n",
+ swidget->widget->name, core, SOF_DSP_PRIMARY_CORE);
+ core = SOF_DSP_PRIMARY_CORE;
+ }
swidget->core = core;
+ }
}
/* bind widget to external event */
@@ -2534,6 +2551,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
if (strstr(file, "dummy")) {
dev_err(scomp->dev,
"Function topology is required, please upgrade sof-firmware\n");
+
+ kfree(tplg_files);
return -EINVAL;
}
tplg_files[0] = file;
@@ -2575,6 +2594,54 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
}
}
+ /* Loading user defined topologies */
+ for (i = 0; i < feature_tplg_cnt; i++) {
+ const char *feature_topology = devm_kasprintf(scomp->dev, GFP_KERNEL, "%s/%s",
+ tplg_filename_prefix,
+ feature_topologies[i]);
+
+ if (!feature_topology) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ dev_info(scomp->dev, "loading feature topology %d: %s\n", i, feature_topology);
+ ret = request_firmware(&fw, feature_topology, scomp->dev);
+ if (ret < 0) {
+ /*
+ * snd_soc_tplg_component_remove(scomp) will be called
+ * if snd_soc_tplg_component_load(scomp) failed and all
+ * objects in the scomp will be removed. No need to call
+ * snd_soc_tplg_component_remove(scomp) here.
+ */
+ dev_warn(scomp->dev, "feature tplg request firmware %s failed err: %d\n",
+ feature_topologies[i], ret);
+ /*
+ * We don't return error here because we can still have the basic
+ * audio feature when the function topology load complete. No need
+ * to convert the error code because we will get new 'ret' out of the
+ * loop.
+ */
+ continue;
+ }
+
+ if (sdev->dspless_mode_selected)
+ ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
+ else
+ ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+
+ release_firmware(fw);
+
+ if (ret < 0) {
+ dev_err(scomp->dev, "feature tplg %s component load failed %d\n",
+ feature_topologies[i], ret);
+ /*
+ * We need to return error here because it may lead to kernel NULL pointer
+ * dereference if we continue the remaining tasks.
+ */
+ goto out;
+ }
+ }
+
/* call sof_complete when topologies are loaded successfully */
ret = sof_complete(scomp);