diff options
Diffstat (limited to 'sound/soc/sof')
| -rw-r--r-- | sound/soc/sof/amd/acp-common.c | 3 | ||||
| -rw-r--r-- | sound/soc/sof/amd/acp-ipc.c | 4 | ||||
| -rw-r--r-- | sound/soc/sof/amd/acp.c | 30 | ||||
| -rw-r--r-- | sound/soc/sof/amd/acp.h | 2 | ||||
| -rw-r--r-- | sound/soc/sof/intel/Kconfig | 6 | ||||
| -rw-r--r-- | sound/soc/sof/intel/hda-mlink.c | 11 | ||||
| -rw-r--r-- | sound/soc/sof/intel/hda-sdw-bpt.c | 31 | ||||
| -rw-r--r-- | sound/soc/sof/intel/hda.c | 14 | ||||
| -rw-r--r-- | sound/soc/sof/intel/lnl.c | 40 | ||||
| -rw-r--r-- | sound/soc/sof/ipc3-control.c | 79 | ||||
| -rw-r--r-- | sound/soc/sof/ipc3-topology.c | 10 | ||||
| -rw-r--r-- | sound/soc/sof/ipc4-control.c | 124 | ||||
| -rw-r--r-- | sound/soc/sof/ipc4-pcm.c | 25 | ||||
| -rw-r--r-- | sound/soc/sof/ipc4-topology.c | 184 | ||||
| -rw-r--r-- | sound/soc/sof/nocodec.c | 4 | ||||
| -rw-r--r-- | sound/soc/sof/sof-audio.c | 9 | ||||
| -rw-r--r-- | sound/soc/sof/sof-client-ipc-flood-test.c | 1 | ||||
| -rw-r--r-- | sound/soc/sof/sof-client-ipc-kernel-injector.c | 1 | ||||
| -rw-r--r-- | sound/soc/sof/sof-client-ipc-msg-injector.c | 1 | ||||
| -rw-r--r-- | sound/soc/sof/sof-client-probes-ipc3.c | 23 | ||||
| -rw-r--r-- | sound/soc/sof/sof-client-probes-ipc4.c | 11 | ||||
| -rw-r--r-- | sound/soc/sof/topology.c | 71 |
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 = ¶ms; - msg->data_size = sizeof(params); + msg.data_ptr = ¶ms; + 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); |
