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.c17
-rw-r--r--sound/soc/sof/amd/acp-pcm.c34
-rw-r--r--sound/soc/sof/amd/acp.c45
-rw-r--r--sound/soc/sof/amd/acp.h7
-rw-r--r--sound/soc/sof/amd/pci-rmb.c91
-rw-r--r--sound/soc/sof/amd/pci-rn.c91
-rw-r--r--sound/soc/sof/amd/rembrandt.c4
-rw-r--r--sound/soc/sof/amd/renoir.c3
-rw-r--r--sound/soc/sof/compress.c8
-rw-r--r--sound/soc/sof/core.c31
-rw-r--r--sound/soc/sof/debug.c1
-rw-r--r--sound/soc/sof/intel/Kconfig7
-rw-r--r--sound/soc/sof/intel/Makefile5
-rw-r--r--sound/soc/sof/intel/apl.c4
-rw-r--r--sound/soc/sof/intel/cnl.c4
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c1
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c9
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c390
-rw-r--r--sound/soc/sof/intel/hda-dai.c698
-rw-r--r--sound/soc/sof/intel/hda-dsp.c101
-rw-r--r--sound/soc/sof/intel/hda-ipc.c3
-rw-r--r--sound/soc/sof/intel/hda-loader.c9
-rw-r--r--sound/soc/sof/intel/hda-mlink.c822
-rw-r--r--sound/soc/sof/intel/hda-pcm.c24
-rw-r--r--sound/soc/sof/intel/hda-stream.c93
-rw-r--r--sound/soc/sof/intel/hda.c233
-rw-r--r--sound/soc/sof/intel/hda.h69
-rw-r--r--sound/soc/sof/intel/icl.c4
-rw-r--r--sound/soc/sof/intel/mtl.c26
-rw-r--r--sound/soc/sof/intel/pci-apl.c2
-rw-r--r--sound/soc/sof/intel/pci-cnl.c3
-rw-r--r--sound/soc/sof/intel/pci-icl.c2
-rw-r--r--sound/soc/sof/intel/pci-mtl.c1
-rw-r--r--sound/soc/sof/intel/pci-skl.c2
-rw-r--r--sound/soc/sof/intel/pci-tgl.c8
-rw-r--r--sound/soc/sof/intel/tgl.c4
-rw-r--r--sound/soc/sof/ipc3-control.c90
-rw-r--r--sound/soc/sof/ipc3-dtrace.c12
-rw-r--r--sound/soc/sof/ipc3-pcm.c7
-rw-r--r--sound/soc/sof/ipc3-topology.c33
-rw-r--r--sound/soc/sof/ipc3.c12
-rw-r--r--sound/soc/sof/ipc4-control.c257
-rw-r--r--sound/soc/sof/ipc4-mtrace.c21
-rw-r--r--sound/soc/sof/ipc4-pcm.c186
-rw-r--r--sound/soc/sof/ipc4-topology.c1224
-rw-r--r--sound/soc/sof/ipc4-topology.h89
-rw-r--r--sound/soc/sof/ipc4.c8
-rw-r--r--sound/soc/sof/loader.c4
-rw-r--r--sound/soc/sof/pcm.c63
-rw-r--r--sound/soc/sof/pm.c13
-rw-r--r--sound/soc/sof/sof-audio.c85
-rw-r--r--sound/soc/sof/sof-audio.h49
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c3
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c12
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c4
-rw-r--r--sound/soc/sof/sof-client.c3
-rw-r--r--sound/soc/sof/sof-client.h4
-rw-r--r--sound/soc/sof/sof-priv.h21
-rw-r--r--sound/soc/sof/topology.c302
59 files changed, 3757 insertions, 1601 deletions
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index bd6c1b198736..df36b411a12e 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -18,22 +18,6 @@
#include "acp-dsp-offset.h"
#include <sound/sof/xtensa.h>
-int acp_dai_probe(struct snd_soc_dai *dai)
-{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
- unsigned int val;
-
- val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->i2s_pin_config_offset);
- if (val != desc->i2s_mode) {
- dev_err(sdev->dev, "I2S Mode is not supported (I2S_PIN_CONFIG: %#x)\n", val);
- return -EINVAL;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_NS(acp_dai_probe, SND_SOC_SOF_AMD_COMMON);
-
/**
* amd_sof_ipc_dump() - This function is called when IPC tx times out.
* @sdev: SOF device.
@@ -187,6 +171,7 @@ struct snd_sof_dsp_ops sof_acp_common_ops = {
.pcm_open = acp_pcm_open,
.pcm_close = acp_pcm_close,
.pcm_hw_params = acp_pcm_hw_params,
+ .pcm_pointer = acp_pcm_pointer,
.hw_info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c
index 727c3a784a20..0828245bbb99 100644
--- a/sound/soc/sof/amd/acp-pcm.c
+++ b/sound/soc/sof/amd/acp-pcm.c
@@ -39,6 +39,7 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr
platform_params->use_phy_address = true;
platform_params->phy_addr = stream->reg_offset;
platform_params->stream_tag = stream->stream_tag;
+ platform_params->cont_update_posn = 1;
/* write buffer size of stream in scratch memory */
@@ -84,3 +85,36 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
return acp_dsp_stream_put(sdev, stream);
}
EXPORT_SYMBOL_NS(acp_pcm_close, SND_SOC_SOF_AMD_COMMON);
+
+snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_component *scomp = sdev->component;
+ struct snd_sof_pcm_stream *stream;
+ struct sof_ipc_stream_posn posn;
+ struct snd_sof_pcm *spcm;
+ snd_pcm_uframes_t pos;
+ int ret;
+
+ spcm = snd_sof_find_spcm_dai(scomp, rtd);
+ if (!spcm) {
+ dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n",
+ rtd->dai_link->id);
+ return 0;
+ }
+
+ stream = &spcm->stream[substream->stream];
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
+ if (ret < 0) {
+ dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
+ return 0;
+ }
+
+ memcpy(&stream->posn, &posn, sizeof(posn));
+ pos = spcm->stream[substream->stream].posn.host_posn;
+ pos = bytes_to_frames(substream->runtime, pos);
+
+ return pos;
+}
+EXPORT_SYMBOL_NS(acp_pcm_pointer, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index d5ccd4d09278..2ae76bcd3590 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -470,33 +470,39 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
unsigned int addr;
int ret;
+ chip = get_chip_info(sdev->pdata);
+ if (!chip) {
+ dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
+ return -EIO;
+ }
adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
GFP_KERNEL);
if (!adata)
return -ENOMEM;
adata->dev = sdev;
+ adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(adata->dmic_dev)) {
+ dev_err(sdev->dev, "failed to register platform for dmic codec\n");
+ return PTR_ERR(adata->dmic_dev);
+ }
addr = pci_resource_start(pci, ACP_DSP_BAR);
sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR));
if (!sdev->bar[ACP_DSP_BAR]) {
dev_err(sdev->dev, "ioremap error\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto unregister_dev;
}
pci_set_master(pci);
sdev->pdata->hw_pdata = adata;
-
- chip = get_chip_info(sdev->pdata);
- if (!chip) {
- dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
- return -EIO;
- }
-
adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
if (!adata->smn_dev) {
dev_err(sdev->dev, "Failed to get host bridge device\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto unregister_dev;
}
sdev->ipc_irq = pci->irq;
@@ -505,16 +511,12 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
if (ret < 0) {
dev_err(sdev->dev, "failed to register IRQ %d\n",
sdev->ipc_irq);
- pci_dev_put(adata->smn_dev);
- return ret;
+ goto free_smn_dev;
}
ret = acp_init(sdev);
- if (ret < 0) {
- free_irq(sdev->ipc_irq, sdev);
- pci_dev_put(adata->smn_dev);
- return ret;
- }
+ if (ret < 0)
+ goto free_ipc_irq;
sdev->dsp_box.offset = 0;
sdev->dsp_box.size = BOX_SIZE_512;
@@ -530,6 +532,14 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
acp_dsp_stream_init(sdev);
return 0;
+
+free_ipc_irq:
+ free_irq(sdev->ipc_irq, sdev);
+free_smn_dev:
+ pci_dev_put(adata->smn_dev);
+unregister_dev:
+ platform_device_unregister(adata->dmic_dev);
+ return ret;
}
EXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
@@ -543,6 +553,9 @@ int amd_sof_acp_remove(struct snd_sof_dev *sdev)
if (sdev->ipc_irq)
free_irq(sdev->ipc_irq, sdev);
+ if (adata->dmic_dev)
+ platform_device_unregister(adata->dmic_dev);
+
return acp_reset(sdev);
}
EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 39165ebf684b..1c535cc6c3a9 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -158,12 +158,10 @@ struct acp_dsp_stream {
struct sof_amd_acp_desc {
unsigned int rev;
unsigned int host_bridge_id;
- unsigned int i2s_mode;
u32 pgfsm_base;
u32 ext_intr_stat;
u32 dsp_intr_base;
u32 sram_pte_offset;
- u32 i2s_pin_config_offset;
u32 hw_semaphore_offset;
u32 acp_clkmux_sel;
u32 fusion_dsp_offset;
@@ -172,6 +170,8 @@ struct sof_amd_acp_desc {
/* Common device data struct for ACP devices */
struct acp_dev_data {
struct snd_sof_dev *dev;
+ /* DMIC device */
+ struct platform_device *dmic_dev;
unsigned int fw_bin_size;
unsigned int fw_data_bin_size;
u32 fw_bin_page_count;
@@ -238,6 +238,8 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream)
int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_sof_platform_stream_params *platform_params);
+snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream);
extern struct snd_sof_dsp_ops sof_acp_common_ops;
@@ -246,7 +248,6 @@ int sof_renoir_ops_init(struct snd_sof_dev *sdev);
extern struct snd_sof_dsp_ops sof_rembrandt_ops;
int sof_rembrandt_ops_init(struct snd_sof_dev *sdev);
-int acp_dai_probe(struct snd_soc_dai *dai);
struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev);
/* Machine configuration */
int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c
index 4e1de462b431..eaf70ea6e556 100644
--- a/sound/soc/sof/amd/pci-rmb.c
+++ b/sound/soc/sof/amd/pci-rmb.c
@@ -26,33 +26,13 @@
#define ACP6x_REG_START 0x1240000
#define ACP6x_REG_END 0x125C000
-static struct platform_device *dmic_dev;
-static struct platform_device *pdev;
-
-static const struct resource rembrandt_res[] = {
- {
- .start = 0,
- .end = ACP6x_REG_END - ACP6x_REG_START,
- .name = "acp_mem",
- .flags = IORESOURCE_MEM,
- },
- {
- .start = 0,
- .end = 0,
- .name = "acp_dai_irq",
- .flags = IORESOURCE_IRQ,
- },
-};
-
static const struct sof_amd_acp_desc rembrandt_chip_info = {
.rev = 6,
.host_bridge_id = HOST_BRIDGE_RMB,
- .i2s_mode = 0x0a,
.pgfsm_base = ACP6X_PGFSM_BASE,
.ext_intr_stat = ACP6X_EXT_INTR_STAT,
.dsp_intr_base = ACP6X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
- .i2s_pin_config_offset = ACP6X_I2S_PIN_CONFIG,
.hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
.acp_clkmux_sel = ACP6X_CLKMUX_SEL,
.fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
@@ -83,84 +63,17 @@ static const struct sof_dev_desc rembrandt_desc = {
static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- struct platform_device_info pdevinfo;
- struct device *dev = &pci->dev;
- const struct resource *res_i2s;
- struct resource *res;
- unsigned int flag, i, addr;
- int ret;
+ unsigned int flag;
flag = snd_amd_acp_find_config(pci);
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
return -ENODEV;
- ret = sof_pci_probe(pci, pci_id);
- if (ret != 0)
- return ret;
-
- dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
- if (IS_ERR(dmic_dev)) {
- dev_err(dev, "failed to create DMIC device\n");
- sof_pci_remove(pci);
- return PTR_ERR(dmic_dev);
- }
-
- /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
- if (flag != FLAG_AMD_SOF_ONLY_DMIC)
- return 0;
-
- addr = pci_resource_start(pci, 0);
- res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(rembrandt_res),
- GFP_KERNEL);
- if (!res) {
- platform_device_unregister(dmic_dev);
- sof_pci_remove(pci);
- return -ENOMEM;
- }
-
- res_i2s = rembrandt_res;
- for (i = 0; i < ARRAY_SIZE(rembrandt_res); i++, res_i2s++) {
- res[i].name = res_i2s->name;
- res[i].flags = res_i2s->flags;
- res[i].start = addr + res_i2s->start;
- res[i].end = addr + res_i2s->end;
- if (res_i2s->flags == IORESOURCE_IRQ) {
- res[i].start = pci->irq;
- res[i].end = res[i].start;
- }
- }
-
- memset(&pdevinfo, 0, sizeof(pdevinfo));
-
- /*
- * We have common PCI driver probe for ACP device but we have to support I2S without SOF
- * for some distributions. Register platform device that will be used to support non dsp
- * ACP's audio ends points on some machines.
- */
- pdevinfo.name = "acp_asoc_rembrandt";
- pdevinfo.id = 0;
- pdevinfo.parent = &pci->dev;
- pdevinfo.num_res = ARRAY_SIZE(rembrandt_res);
- pdevinfo.res = &res[0];
-
- pdev = platform_device_register_full(&pdevinfo);
- if (IS_ERR(pdev)) {
- dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
- platform_device_unregister(dmic_dev);
- sof_pci_remove(pci);
- ret = PTR_ERR(pdev);
- }
-
- return ret;
+ return sof_pci_probe(pci, pci_id);
};
static void acp_pci_rmb_remove(struct pci_dev *pci)
{
- if (dmic_dev)
- platform_device_unregister(dmic_dev);
- if (pdev)
- platform_device_unregister(pdev);
-
sof_pci_remove(pci);
}
diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c
index fca40b261671..4809cb644152 100644
--- a/sound/soc/sof/amd/pci-rn.c
+++ b/sound/soc/sof/amd/pci-rn.c
@@ -26,33 +26,13 @@
#define ACP3x_REG_START 0x1240000
#define ACP3x_REG_END 0x125C000
-static struct platform_device *dmic_dev;
-static struct platform_device *pdev;
-
-static const struct resource renoir_res[] = {
- {
- .start = 0,
- .end = ACP3x_REG_END - ACP3x_REG_START,
- .name = "acp_mem",
- .flags = IORESOURCE_MEM,
- },
- {
- .start = 0,
- .end = 0,
- .name = "acp_dai_irq",
- .flags = IORESOURCE_IRQ,
- },
-};
-
static const struct sof_amd_acp_desc renoir_chip_info = {
.rev = 3,
.host_bridge_id = HOST_BRIDGE_CZN,
- .i2s_mode = 0x04,
.pgfsm_base = ACP3X_PGFSM_BASE,
.ext_intr_stat = ACP3X_EXT_INTR_STAT,
.dsp_intr_base = ACP3X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP3X_SRAM_PTE_OFFSET,
- .i2s_pin_config_offset = ACP3X_I2S_PIN_CONFIG,
.hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0,
.acp_clkmux_sel = ACP3X_CLKMUX_SEL,
};
@@ -83,84 +63,17 @@ static const struct sof_dev_desc renoir_desc = {
static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- struct platform_device_info pdevinfo;
- struct device *dev = &pci->dev;
- const struct resource *res_i2s;
- struct resource *res;
- unsigned int flag, i, addr;
- int ret;
+ unsigned int flag;
flag = snd_amd_acp_find_config(pci);
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
return -ENODEV;
- ret = sof_pci_probe(pci, pci_id);
- if (ret != 0)
- return ret;
-
- dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
- if (IS_ERR(dmic_dev)) {
- dev_err(dev, "failed to create DMIC device\n");
- sof_pci_remove(pci);
- return PTR_ERR(dmic_dev);
- }
-
- /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
- if (flag != FLAG_AMD_SOF_ONLY_DMIC)
- return 0;
-
- addr = pci_resource_start(pci, 0);
- res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
- if (!res) {
- sof_pci_remove(pci);
- platform_device_unregister(dmic_dev);
- return -ENOMEM;
- }
-
- res_i2s = renoir_res;
- for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) {
- res[i].name = res_i2s->name;
- res[i].flags = res_i2s->flags;
- res[i].start = addr + res_i2s->start;
- res[i].end = addr + res_i2s->end;
- if (res_i2s->flags == IORESOURCE_IRQ) {
- res[i].start = pci->irq;
- res[i].end = res[i].start;
- }
- }
-
- memset(&pdevinfo, 0, sizeof(pdevinfo));
-
- /*
- * We have common PCI driver probe for ACP device but we have to support I2S without SOF
- * for some distributions. Register platform device that will be used to support non dsp
- * ACP's audio ends points on some machines.
- */
-
- pdevinfo.name = "acp_asoc_renoir";
- pdevinfo.id = 0;
- pdevinfo.parent = &pci->dev;
- pdevinfo.num_res = ARRAY_SIZE(renoir_res);
- pdevinfo.res = &res[0];
-
- pdev = platform_device_register_full(&pdevinfo);
- if (IS_ERR(pdev)) {
- dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
- sof_pci_remove(pci);
- platform_device_unregister(dmic_dev);
- ret = PTR_ERR(pdev);
- }
-
- return ret;
+ return sof_pci_probe(pci, pci_id);
};
static void acp_pci_rn_remove(struct pci_dev *pci)
{
- if (dmic_dev)
- platform_device_unregister(dmic_dev);
- if (pdev)
- platform_device_unregister(pdev);
-
return sof_pci_remove(pci);
}
diff --git a/sound/soc/sof/amd/rembrandt.c b/sound/soc/sof/amd/rembrandt.c
index 5288ab882fc9..f1d1ba57ab3a 100644
--- a/sound/soc/sof/amd/rembrandt.c
+++ b/sound/soc/sof/amd/rembrandt.c
@@ -48,7 +48,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
.rate_min = 8000,
.rate_max = 48000,
},
- .probe = &acp_dai_probe,
},
[I2S_BT_INSTANCE] = {
@@ -73,7 +72,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
.rate_min = 8000,
.rate_max = 48000,
},
- .probe = &acp_dai_probe,
},
[I2S_SP_INSTANCE] = {
@@ -98,7 +96,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
.rate_min = 8000,
.rate_max = 48000,
},
- .probe = &acp_dai_probe,
},
[PDM_DMIC_INSTANCE] = {
@@ -126,7 +123,6 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = {
.rate_min = 8000,
.rate_max = 96000,
},
- .probe = &acp_dai_probe,
},
};
diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c
index adade2e3d3be..47b863f6258c 100644
--- a/sound/soc/sof/amd/renoir.c
+++ b/sound/soc/sof/amd/renoir.c
@@ -47,7 +47,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
.rate_min = 8000,
.rate_max = 48000,
},
- .probe = &acp_dai_probe,
},
[I2S_SP_INSTANCE] = {
@@ -72,7 +71,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
.rate_min = 8000,
.rate_max = 48000,
},
- .probe = &acp_dai_probe,
},
[PDM_DMIC_INSTANCE] = {
@@ -100,7 +98,6 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = {
.rate_min = 8000,
.rate_max = 96000,
},
- .probe = &acp_dai_probe,
},
};
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 8d205eb16d2f..d7b044f33d79 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -135,7 +135,6 @@ static int sof_compr_free(struct snd_soc_component *component,
struct sof_compr_stream *sstream = cstream->runtime->private_data;
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
struct snd_sof_pcm *spcm;
int ret = 0;
@@ -148,8 +147,7 @@ static int sof_compr_free(struct snd_soc_component *component,
stream.comp_id = spcm->stream[cstream->direction].comp_id;
if (spcm->prepared[cstream->direction]) {
- ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
if (!ret)
spcm->prepared[cstream->direction] = false;
}
@@ -273,7 +271,6 @@ static int sof_compr_trigger(struct snd_soc_component *component,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
struct snd_sof_pcm *spcm;
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -302,8 +299,7 @@ static int sof_compr_trigger(struct snd_soc_component *component,
break;
}
- return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
- &reply, sizeof(reply));
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
}
static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 7de8673a01ce..9a9d82220fd0 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -208,6 +208,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
/* set up platform component driver */
snd_sof_new_platform_drv(sdev);
+ if (sdev->dspless_mode_selected) {
+ sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ goto skip_dsp_init;
+ }
+
/* register any debug/trace capabilities */
ret = snd_sof_dbg_init(sdev);
if (ret < 0) {
@@ -266,6 +271,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
}
+skip_dsp_init:
/* hereafter all FW boot flows are for PM reasons */
sdev->first_boot = false;
@@ -365,6 +371,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
if (sof_core_debug)
dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
+ if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) {
+ if (plat_data->desc->dspless_mode_supported) {
+ dev_info(dev, "Switching to DSPless mode\n");
+ sdev->dspless_mode_selected = true;
+ } else {
+ dev_info(dev, "DSPless mode is not supported by the platform\n");
+ }
+ }
+
/* check IPC support */
if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
@@ -378,12 +393,18 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
return ret;
/* check all mandatory ops */
- if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
- !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
- !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
- !sof_ops(sdev)->ipc_msg_data) {
+ if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
+ sof_ops_free(sdev);
+ dev_err(dev, "missing mandatory ops\n");
+ return -EINVAL;
+ }
+
+ if (!sdev->dspless_mode_selected &&
+ (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
+ !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
+ !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
sof_ops_free(sdev);
- dev_err(dev, "error: missing mandatory ops\n");
+ dev_err(dev, "missing mandatory DSP ops\n");
return -EINVAL;
}
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index ade0507328af..b42b5982cbbc 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -370,6 +370,7 @@ static const struct soc_fw_state_info {
const char *name;
} fw_state_dbg[] = {
{SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
+ {SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"},
{SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"},
{SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"},
{SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"},
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 715ba8a7f2f8..f4eeacf1f281 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -269,6 +269,13 @@ config SND_SOC_SOF_HDA_COMMON
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
select SND_SOC_SOF_HDA_PROBES
+ select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_HDA_MLINK
+ tristate
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index 8201cbdd654c..fdb463c12e91 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -5,10 +5,12 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
- hda-dai.o hda-bus.o hda-mlink.o \
+ hda-dai.o hda-dai-ops.o hda-bus.o \
skl.o hda-loader-skl.o \
apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o
+snd-sof-intel-hda-mlink-objs := hda-mlink.o
+
snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
snd-sof-intel-hda-objs := hda-codec.o
@@ -19,6 +21,7 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o
obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o
obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o
obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
+obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o
obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
snd-sof-pci-intel-tng-objs := pci-tng.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 0e7a7e4ad976..e1f25a8f0c32 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -48,6 +48,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_apl_ops.ipc_dump = hda_ipc_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -73,6 +75,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_apl_ops.ipc_dump = hda_ipc4_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index a08a77fa946b..a95222e53ecf 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -395,6 +395,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_cnl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -420,6 +422,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_cnl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
index 397303b3ac9d..8e1cd0babd32 100644
--- a/sound/soc/sof/intel/hda-common-ops.c
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -89,7 +89,6 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
.runtime_resume = hda_dsp_runtime_resume,
.runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
/* ALSA HW info flags */
.hw_info = SNDRV_PCM_INFO_MMAP |
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index f3bdeba28412..84bf01bd360a 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -19,6 +19,7 @@
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/hda_component.h>
+#include <sound/hda-mlink.h>
#include "../ops.h"
#include "hda.h"
@@ -158,16 +159,18 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
*/
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
u32 val;
/* enable/disable audio dsp clock gating */
val = enable ? PCI_CGCTL_ADSPDCGE : 0;
snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val);
- /* enable/disable DMI Link L1 support */
+ /* disable the DMI link when requested. But enable only if it wasn't disabled previously */
val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN, val);
+ if (!enable || !hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, val);
/* enable/disable audio dsp power gating */
val = enable ? 0 : PCI_PGCTL_ADSPPGD;
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
new file mode 100644
index 000000000000..4b39cecacd68
--- /dev/null
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <sound/pcm_params.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/sof/ipc4/header.h>
+#include <uapi/sound/sof/header.h>
+#include "../ipc4-priv.h"
+#include "../ipc4-topology.h"
+#include "../sof-priv.h"
+#include "../sof-audio.h"
+#include "hda.h"
+
+/* These ops are only applicable for the HDA DAI's in their current form */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+/*
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
+ */
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+ int dir, int stream_tag)
+{
+ struct snd_pcm_substream *fe_substream;
+ struct hdac_stream *fe_hstream;
+ struct snd_soc_dpcm *dpcm;
+
+ for_each_dpcm_fe(rtd, dir, dpcm) {
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+ fe_hstream = fe_substream->runtime->private_data;
+ if (fe_hstream->stream_tag == stream_tag)
+ return true;
+ }
+
+ return false;
+}
+
+static struct hdac_ext_stream *
+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_intel_hda_stream *hda_stream;
+ const struct sof_intel_dsp_desc *chip;
+ struct snd_sof_dev *sdev;
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ int stream_dir = substream->stream;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream =
+ stream_to_hdac_ext_stream(hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ sdev = hda_stream->sdev;
+ chip = get_chip_info(sdev->pdata);
+
+ /* check if link is available */
+ if (!hext_stream->link_locked) {
+ /*
+ * choose the first available link for platforms that do not have the
+ * PROCEN_FMT_QUIRK set.
+ */
+ if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
+ res = hext_stream;
+ break;
+ }
+
+ if (hstream->opened) {
+ /*
+ * check if the stream tag matches the stream
+ * tag of one of the connected FEs
+ */
+ if (hda_check_fes(rtd, stream_dir,
+ hstream->stream_tag)) {
+ res = hext_stream;
+ break;
+ }
+ } else {
+ res = hext_stream;
+
+ /*
+ * This must be a hostless stream.
+ * So reserve the host DMA channel.
+ */
+ hda_stream->host_reserved = 1;
+ break;
+ }
+ }
+ }
+
+ if (res) {
+ /* Make sure that host and link DMA is decoupled. */
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
+ res->link_locked = 1;
+ res->link_substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+
+static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
+static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream;
+
+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
+ if (!hext_stream)
+ return NULL;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+
+ return hext_stream;
+}
+
+static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+}
+
+static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val)
+{
+ snd_hdac_ext_stream_setup(hext_stream, format_val);
+}
+
+static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_ext_stream_reset(hext_stream);
+}
+
+static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_stream_start(hext_stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_hdac_ext_stream_clear(hext_stream);
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ }
+
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RESET);
+ if (ret < 0)
+ return ret;
+
+ pipeline->state = SOF_IPC4_PIPE_RESET;
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger
+};
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+};
+
+static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ struct snd_sof_dai_config_data data = { 0 };
+
+ data.dai_data = DMA_CHAN_INVALID;
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc3_post_trigger,
+};
+
+static struct hdac_ext_stream *
+hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+
+ return stream_to_hdac_ext_stream(hstream);
+}
+
+static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
+ struct hdac_ext_stream *hext_stream,
+ unsigned int format_val)
+{
+ /*
+ * Save the format_val which was adjusted by the maxbps of the codec.
+ * This information is not available on the FE side since there we are
+ * using dummy_codec.
+ */
+ hext_stream->hstream.format_val = format_val;
+}
+
+static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
+ .get_hext_stream = hda_dspless_get_hext_stream,
+ .setup_hext_stream = hda_dspless_setup_hext_stream,
+};
+
+#endif
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+ struct snd_sof_dai *sdai;
+
+ if (sdev->dspless_mode_selected)
+ return &hda_dspless_dma_ops;
+
+ sdai = swidget->private;
+
+ switch (sdev->pdata->ipc_type) {
+ case SOF_IPC:
+ {
+ struct sof_dai_private_data *private = sdai->private;
+
+ if (private->dai_config->type == SOF_DAI_INTEL_HDA)
+ return &hda_ipc3_dma_ops;
+ break;
+ }
+ case SOF_INTEL_IPC4:
+ {
+ struct sof_ipc4_copier *ipc4_copier = sdai->private;
+
+ if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->use_chain_dma)
+ return &hda_ipc4_chain_dma_ops;
+
+ return &hda_ipc4_dma_ops;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return NULL;
+}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 8d9c38d562d3..44a5d94c5050 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -27,119 +27,77 @@ static bool hda_use_tplg_nhlt;
module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ const struct sof_ipc_tplg_ops *tplg_ops;
+ struct snd_soc_component *component;
+ struct snd_sof_dev *sdev;
+ int ret;
-struct hda_pipe_params {
- u32 ch;
- u32 s_freq;
- snd_pcm_format_t format;
- int link_index;
- unsigned int link_bps;
-};
+ if (!swidget)
+ return 0;
-/*
- * This function checks if the host dma channel corresponding
- * to the link DMA stream_tag argument is assigned to one
- * of the FEs connected to the BE DAI.
- */
-static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
- int dir, int stream_tag)
-{
- struct snd_pcm_substream *fe_substream;
- struct hdac_stream *fe_hstream;
- struct snd_soc_dpcm *dpcm;
-
- for_each_dpcm_fe(rtd, dir, dpcm) {
- fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
- fe_hstream = fe_substream->runtime->private_data;
- if (fe_hstream->stream_tag == stream_tag)
- return true;
+ component = swidget->scomp;
+ sdev = snd_soc_component_get_drvdata(component);
+ tplg_ops = sof_ipc_get_ops(sdev, tplg);
+
+ if (tplg_ops && tplg_ops->dai_config) {
+ ret = tplg_ops->dai_config(sdev, swidget, flags, data);
+ if (ret < 0) {
+ dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
+ flags, w->name);
+ return ret;
+ }
}
- return false;
+ return 0;
}
-static struct hdac_ext_stream *
-hda_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct sof_intel_hda_stream *hda_stream;
- const struct sof_intel_dsp_desc *chip;
- struct snd_sof_dev *sdev;
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *hstream = NULL;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- int stream_dir = substream->stream;
+static const struct hda_dai_widget_dma_ops *
+hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *sdai;
- if (!bus->ppcap) {
- dev_err(bus->dev, "stream type not supported\n");
- return NULL;
- }
+ /*
+ * The swidget parameter of hda_select_dai_widget_ops() is ignored in
+ * case of DSPless mode
+ */
+ if (sdev->dspless_mode_selected)
+ return hda_select_dai_widget_ops(sdev, NULL);
- spin_lock_irq(&bus->reg_lock);
- list_for_each_entry(hstream, &bus->stream_list, list) {
- struct hdac_ext_stream *hext_stream =
- stream_to_hdac_ext_stream(hstream);
- if (hstream->direction != substream->stream)
- continue;
-
- hda_stream = hstream_to_sof_hda_stream(hext_stream);
- sdev = hda_stream->sdev;
- chip = get_chip_info(sdev->pdata);
-
- /* check if link is available */
- if (!hext_stream->link_locked) {
- /*
- * choose the first available link for platforms that do not have the
- * PROCEN_FMT_QUIRK set.
- */
- if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
- res = hext_stream;
- break;
- }
+ sdai = swidget->private;
- if (hstream->opened) {
- /*
- * check if the stream tag matches the stream
- * tag of one of the connected FEs
- */
- if (hda_check_fes(rtd, stream_dir,
- hstream->stream_tag)) {
- res = hext_stream;
- break;
- }
- } else {
- res = hext_stream;
-
- /*
- * This must be a hostless stream.
- * So reserve the host DMA channel.
- */
- hda_stream->host_reserved = 1;
- break;
- }
- }
- }
+ /* select and set the DAI widget ops if not set already */
+ if (!sdai->platform_private) {
+ const struct hda_dai_widget_dma_ops *ops =
+ hda_select_dai_widget_ops(sdev, swidget);
+ if (!ops)
+ return NULL;
- if (res) {
- /* Make sure that host and link DMA is decoupled. */
- snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ /* check if mandatory ops are set */
+ if (!ops || !ops->get_hext_stream)
+ return NULL;
- res->link_locked = 1;
- res->link_substream = substream;
+ sdai->platform_private = ops;
}
- spin_unlock_irq(&bus->reg_lock);
- return res;
+ return sdai->platform_private;
}
static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
struct hdac_ext_stream *hext_stream,
struct snd_soc_dai *cpu_dai,
- struct snd_soc_dai *codec_dai,
- bool trigger_suspend_stop)
+ struct snd_soc_dai *codec_dai)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
struct hdac_stream *hstream = &hext_stream->hstream;
struct hdac_bus *bus = hstream->bus;
struct sof_intel_hda_stream *hda_stream;
@@ -150,15 +108,14 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
if (!hlink)
return -EINVAL;
- if (trigger_suspend_stop)
- snd_hdac_ext_stream_clear(hext_stream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
stream_tag = hdac_stream(hext_stream)->stream_tag;
snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
}
- snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
- snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+
+ if (ops->release_hext_stream)
+ ops->release_hext_stream(sdev, cpu_dai, substream);
+
hext_stream->link_prepared = 0;
/* free the host DMA channel reserved by hostless streams */
@@ -168,50 +125,20 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
return 0;
}
-static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
- struct hda_pipe_params *params)
-{
- struct hdac_stream *hstream = &hext_stream->hstream;
- unsigned char stream_tag = hstream->stream_tag;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_link *hlink;
- unsigned int format_val;
-
- snd_hdac_ext_stream_reset(hext_stream);
-
- format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
- params->format,
- params->link_bps, 0);
-
- dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
- format_val, params->s_freq, params->ch, params->format);
-
- snd_hdac_ext_stream_setup(hext_stream, format_val);
-
- if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
- list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (hlink->index == params->link_index)
- snd_hdac_ext_bus_link_set_stream_id(hlink,
- stream_tag);
- }
- }
-
- hext_stream->link_prepared = 1;
-
- return 0;
-}
-
static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
{
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hda_pipe_params p_params = {0};
struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
struct hdac_ext_link *hlink;
struct snd_sof_dev *sdev;
struct hdac_bus *bus;
+ unsigned int format_val;
+ unsigned int link_bps;
+ int stream_tag;
sdev = snd_soc_component_get_drvdata(cpu_dai->component);
bus = sof_to_bus(sdev);
@@ -220,337 +147,196 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
if (!hlink)
return -EINVAL;
- hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- if (!hext_stream) {
- hext_stream = hda_link_stream_assign(bus, substream);
- if (!hext_stream)
- return -EBUSY;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+ if (!hext_stream) {
+ if (ops->assign_hext_stream)
+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
}
+ if (!hext_stream)
+ return -EBUSY;
+
+ hstream = &hext_stream->hstream;
+ stream_tag = hstream->stream_tag;
+
+ if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
+
/* set the hdac_stream in the codec dai */
snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.link_index = hlink->index;
- p_params.format = params_format(params);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- p_params.link_bps = codec_dai->driver->playback.sig_bits;
+ link_bps = codec_dai->driver->playback.sig_bits;
else
- p_params.link_bps = codec_dai->driver->capture.sig_bits;
+ link_bps = codec_dai->driver->capture.sig_bits;
- return hda_link_dma_params(hext_stream, &p_params);
-}
+ if (ops->reset_hext_stream)
+ ops->reset_hext_stream(sdev, hext_stream);
-static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- int stream = substream->stream;
+ format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
+ params_format(params), link_bps, 0);
- return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
-}
+ dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), params_channels(params), params_format(params));
-static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- int ret;
+ if (ops->setup_hext_stream)
+ ops->setup_hext_stream(sdev, hext_stream, format_val);
- if (!hext_stream)
- return 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_stream_start(hext_stream);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, true);
- if (ret < 0)
- return ret;
-
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_stream_clear(hext_stream);
+ hext_stream->link_prepared = 1;
- break;
- default:
- return -EINVAL;
- }
return 0;
}
-static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
+static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hdac_ext_stream *hext_stream;
-
- hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- if (!hext_stream)
- return 0;
-
- return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
-}
-
-static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
- int channel, bool widget_setup)
-{
- struct snd_sof_dai_config_data data;
-
- data.dai_data = channel;
-
- /* set up/free DAI widget and send DAI_CONFIG IPC */
- if (widget_setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
+ int stream = substream->stream;
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+ return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
}
-static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct hdac_ext_stream *hext_stream;
- struct snd_soc_dapm_widget *w;
- int stream_tag;
- hext_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!hext_stream)
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
return -EINVAL;
+ }
- stream_tag = hdac_stream(hext_stream)->stream_tag;
-
- w = snd_soc_dai_get_widget(dai, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return 0;
- /* set up the DAI widget and send the DAI_CONFIG with the new tag */
- return hda_dai_widget_update(w, stream_tag - 1, true);
+ return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
}
static int hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream =
- snd_soc_dai_get_dma_data(dai, substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
int ret;
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (hext_stream && hext_stream->link_prepared)
return 0;
- ret = hda_link_dma_hw_params(substream, params);
+ ret = hda_link_dma_hw_params(substream, params, dai);
if (ret < 0)
return ret;
- return hda_dai_hw_params_update(substream, params, dai);
-}
-
-
-static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- int ret = 0;
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
- if (tplg_ops->dai_config) {
- ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
- if (ret < 0)
- dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
- w->name);
- }
+ flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
- return ret;
+ return hda_dai_config(w, flags, &data);
}
static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream =
- snd_soc_dai_get_dma_data(dai, substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- int stream = substream->stream;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
int ret;
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (hext_stream && hext_stream->link_prepared)
return 0;
dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
- ret = hda_link_dma_prepare(substream);
- if (ret < 0)
- return ret;
-
- return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
-}
-
-static int hda_dai_hw_free_ipc(int stream, /* direction */
- struct snd_soc_dai *dai)
-{
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(dai, stream);
-
- /* free the link DMA channel in the FW and the DAI widget */
- return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
-}
-
-static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct snd_soc_dapm_widget *w;
- int ret;
-
- dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
- dai->name, substream->stream);
-
- ret = hda_link_dma_trigger(substream, cmd);
+ ret = hda_link_dma_prepare(substream, dai);
if (ret < 0)
return ret;
- w = snd_soc_dai_get_widget(dai, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- /*
- * free DAI widget during stop/suspend to keep widget use_count's balanced.
- */
- ret = hda_dai_hw_free_ipc(substream->stream, dai);
- if (ret < 0)
- return ret;
-
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = hda_dai_config_pause_push_ipc(w);
- if (ret < 0)
- return ret;
- break;
+ flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
- default:
- break;
- }
- return 0;
+ return hda_dai_config(w, flags, &data);
}
/*
* In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
* (over IPC channel) and DMA state change (direct host register changes).
*/
-static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
struct snd_soc_pcm_runtime *rtd;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai;
int ret;
dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
dai->name, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (!hext_stream)
+ return -EINVAL;
+
rtd = asoc_substream_to_rtd(substream);
- cpu_dai = asoc_rtd_to_cpu(rtd, 0);
codec_dai = asoc_rtd_to_codec(rtd, 0);
- w = snd_soc_dai_get_widget(dai, substream->stream);
- swidget = w->dobj.private;
- pipe_widget = swidget->spipe->pipe_widget;
- pipeline = pipe_widget->private;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_stream_start(hext_stream);
- if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
- }
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RUNNING);
+ if (ops->pre_trigger) {
+ ret = ops->pre_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
- pipeline->state = SOF_IPC4_PIPE_RUNNING;
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
+ }
+
+ if (ops->trigger) {
+ ret = ops->trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- snd_hdac_ext_stream_clear(hext_stream);
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RESET);
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
- pipeline->state = SOF_IPC4_PIPE_RESET;
-
- ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
if (ret < 0) {
dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
return ret;
}
break;
- }
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
-
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- snd_hdac_ext_stream_clear(hext_stream);
- break;
- }
default:
- dev_err(sdev->dev, "%s: unknown trigger command %d\n", __func__, cmd);
- return -EINVAL;
+ break;
}
return 0;
}
-static int hda_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret;
-
- ret = hda_link_dma_hw_free(substream);
- if (ret < 0)
- return ret;
-
- return hda_dai_hw_free_ipc(substream->stream, dai);
-}
-
-static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
+static const struct snd_soc_dai_ops hda_dai_ops = {
.hw_params = hda_dai_hw_params,
.hw_free = hda_dai_hw_free,
- .trigger = ipc3_hda_dai_trigger,
+ .trigger = hda_dai_trigger,
.prepare = hda_dai_prepare,
};
@@ -573,186 +359,62 @@ static int hda_dai_suspend(struct hdac_bus *bus)
* explicitly during suspend.
*/
if (hext_stream->link_substream) {
- struct snd_soc_dai *cpu_dai;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
rtd = asoc_substream_to_rtd(hext_stream->link_substream);
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
codec_dai = asoc_rtd_to_codec(rtd, 0);
+ w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
+ swidget = w->dobj.private;
+ sdev = snd_soc_component_get_drvdata(swidget->scomp);
+ sdai = swidget->private;
+ ops = sdai->platform_private;
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
- cpu_dai, codec_dai, false);
+ cpu_dai, codec_dai);
if (ret < 0)
return ret;
- /* for consistency with TRIGGER_SUSPEND we free DAI resources */
- ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
- if (ret < 0)
- return ret;
+ /* for consistency with TRIGGER_SUSPEND */
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, cpu_dai,
+ hext_stream->link_substream,
+ SNDRV_PCM_TRIGGER_SUSPEND);
+ if (ret < 0)
+ return ret;
+ }
}
}
return 0;
}
-static const struct snd_soc_dai_ops ipc4_hda_dai_ops = {
- .hw_params = hda_dai_hw_params,
- .hw_free = hda_dai_hw_free,
- .trigger = ipc4_hda_dai_trigger,
- .prepare = hda_dai_prepare,
-};
-
#endif
-/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
-struct ssp_dai_dma_data {
- bool setup;
-};
-
-static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
- bool setup)
-{
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(dai, substream->stream);
-
- if (setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-}
-
-static int ssp_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ssp_dai_dma_data *dma_data;
-
- dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
- if (!dma_data)
- return -ENOMEM;
-
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
-
- return 0;
-}
-
-static int ssp_dai_setup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai,
- bool setup)
-{
- struct ssp_dai_dma_data *dma_data;
- int ret = 0;
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma_data) {
- dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
- return -EIO;
- }
-
- if (dma_data->setup != setup) {
- ret = ssp_dai_setup_or_free(substream, dai, setup);
- if (!ret)
- dma_data->setup = setup;
- }
- return ret;
-}
-
-static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- /* params are ignored for now */
- return ssp_dai_setup(substream, dai, true);
-}
-
-static int ssp_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- /*
- * the SSP will only be reconfigured during resume operations and
- * not in case of xruns
- */
- return ssp_dai_setup(substream, dai, true);
-}
-
-static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
- return 0;
-
- return ssp_dai_setup(substream, dai, false);
-}
-
-static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- return ssp_dai_setup(substream, dai, false);
-}
-
-static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ssp_dai_dma_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma_data) {
- dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
- return;
- }
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(dma_data);
-}
-
-static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
- .startup = ssp_dai_startup,
- .hw_params = ssp_dai_hw_params,
- .prepare = ssp_dai_prepare,
- .trigger = ipc3_ssp_dai_trigger,
- .hw_free = ssp_dai_hw_free,
- .shutdown = ssp_dai_shutdown,
-};
-
void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
int i;
- switch (sdev->pdata->ipc_type) {
- case SOF_IPC:
- for (i = 0; i < ops->num_drv; i++) {
- if (strstr(ops->drv[i].name, "SSP")) {
- ops->drv[i].ops = &ipc3_ssp_dai_ops;
- continue;
- }
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- if (strstr(ops->drv[i].name, "iDisp") ||
- strstr(ops->drv[i].name, "Analog") ||
- strstr(ops->drv[i].name, "Digital"))
- ops->drv[i].ops = &ipc3_hda_dai_ops;
-#endif
- }
- break;
- case SOF_INTEL_IPC4:
- {
- struct sof_ipc4_fw_data *ipc4_data = sdev->private;
-
- for (i = 0; i < ops->num_drv; i++) {
+ for (i = 0; i < ops->num_drv; i++) {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- if (strstr(ops->drv[i].name, "iDisp") ||
- strstr(ops->drv[i].name, "Analog") ||
- strstr(ops->drv[i].name, "Digital"))
- ops->drv[i].ops = &ipc4_hda_dai_ops;
+ if (strstr(ops->drv[i].name, "iDisp") ||
+ strstr(ops->drv[i].name, "Analog") ||
+ strstr(ops->drv[i].name, "Digital"))
+ ops->drv[i].ops = &hda_dai_ops;
#endif
- }
+ }
- if (!hda_use_tplg_nhlt)
- ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
- break;
- }
- default:
- break;
+ ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
}
}
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index a6f2822401e0..44f39a520bb3 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
#include <trace/events/sof_intel.h>
#include "../sof-audio.h"
#include "../ops.h"
@@ -321,6 +322,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* enable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY,
@@ -336,6 +340,9 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* disable IPC interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, 0);
@@ -567,31 +574,11 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
* is called again either because of a new IPC sent to the DSP or
* during system suspend/resume.
*/
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
+static int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
{
int ret = 0;
- /*
- * When the DSP is already in D0I3 and the target state is D0I3,
- * it could be the case that the DSP is in D0I3 during S0
- * and the system is suspending to S0Ix. Therefore,
- * hda_dsp_set_D0_state() must be called to disable trace DMA
- * by sending the PM_GATE IPC to the FW.
- */
- if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
- sdev->system_suspend_target == SOF_SUSPEND_S0IX)
- goto set_state;
-
- /*
- * For all other cases, return without doing anything if
- * the DSP is already in the target state.
- */
- if (target_state->state == sdev->dsp_power_state.state &&
- target_state->substate == sdev->dsp_power_state.substate)
- return 0;
-
-set_state:
switch (target_state->state) {
case SOF_DSP_PM_D0:
ret = hda_dsp_set_D0_state(sdev, target_state);
@@ -623,6 +610,42 @@ set_state:
return ret;
}
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /*
+ * When the DSP is already in D0I3 and the target state is D0I3,
+ * it could be the case that the DSP is in D0I3 during S0
+ * and the system is suspending to S0Ix. Therefore,
+ * hda_dsp_set_D0_state() must be called to disable trace DMA
+ * by sending the PM_GATE IPC to the FW.
+ */
+ if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
+ sdev->system_suspend_target == SOF_SUSPEND_S0IX)
+ return hda_dsp_set_power_state(sdev, target_state);
+
+ /*
+ * For all other cases, return without doing anything if
+ * the DSP is already in the target state.
+ */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /* Return without doing anything if the DSP is already in the target state */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+
/*
* Audio DSP states may transform as below:-
*
@@ -681,6 +704,9 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* power down all hda links */
hda_bus_ml_suspend(bus);
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp;
+
ret = chip->power_down_dsp(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to power down DSP during suspend\n");
@@ -694,6 +720,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+skip_dsp:
/* disable hda bus irq and streams */
hda_dsp_ctrl_stop_chip(sdev);
@@ -744,9 +771,11 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_codec_jack_check(sdev);
}
- /* enable ppcap interrupt */
- hda_dsp_ctrl_ppcap_enable(sdev, true);
- hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ if (!sdev->dspless_mode_selected) {
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ }
cleanup:
/* display codec can powered off after controller init */
@@ -788,7 +817,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev)
}
/* restore L1SEN bit */
- if (hda->l1_support_changed)
+ if (hda->l1_disabled)
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
@@ -843,8 +872,10 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
};
int ret;
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected) {
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+ }
/* stop hda controller and power dsp off */
ret = hda_suspend(sdev, true);
@@ -866,8 +897,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
};
int ret;
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected) {
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+ }
if (target_state == SOF_DSP_PM_D0) {
/* Set DSP power state */
@@ -880,11 +913,9 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
}
/* enable L1SEN to make sure the system can enter S0Ix */
- hda->l1_support_changed =
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN,
- HDA_VS_INTEL_EM2_L1SEN);
+ if (hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
/* stop the CORB/RIRB DMA if it is On */
hda_codec_suspend_cmd_io(sdev);
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index df541b22b2d2..a838dddb1d32 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -355,6 +355,9 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
bool ret = false;
u32 irq_status;
+ if (sdev->dspless_mode_selected)
+ return false;
+
/* store status */
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index d680562edb35..50ce6b190002 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -321,13 +321,13 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
{
struct hdac_ext_stream *iccmax_stream;
- struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_dma_buffer dmab_bdl;
int ret, ret1;
u8 original_gb;
/* save the original LTRP guardband value */
- original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK;
+ original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) &
+ HDA_VS_INTEL_LTRP_GB_MASK;
/*
* Prepare capture stream for ICCMAX. We do not need to store
@@ -356,7 +356,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
}
/* restore the original guardband value after FW boot */
- snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
+ snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
+ HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
return ret;
}
@@ -556,7 +557,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
goto cleanup;
}
- ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
if (ret1 < 0) {
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index 76ab9a2e7bb3..775582ab7494 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -12,49 +12,746 @@
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
-#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/module.h>
-#include <linux/soundwire/sdw.h>
-#include <linux/soundwire/sdw_intel.h>
-#include <sound/intel-dsp-config.h>
-#include <sound/intel-nhlt.h>
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-#include "../sof-audio.h"
-#include "../sof-pci-dev.h"
-#include "../ops.h"
-#include "hda.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus)
+/**
+ * struct hdac_ext2_link - HDAudio extended+alternate link
+ *
+ * @hext_link: hdac_ext_link
+ * @alt: flag set for alternate extended links
+ * @intc: boolean for interrupt capable
+ * @ofls: boolean for offload support
+ * @lss: boolean for link synchronization capabilities
+ * @slcount: sublink count
+ * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
+ * @elver: extended link version
+ * @leptr: extended link pointer
+ * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
+ * in LCTL register
+ * @base_ptr: pointer to shim/ip/shim_vs space
+ * @instance_offset: offset between each of @slcount instances managed by link
+ * @shim_offset: offset to SHIM register base
+ * @ip_offset: offset to IP register base
+ * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
+ */
+struct hdac_ext2_link {
+ struct hdac_ext_link hext_link;
+
+ /* read directly from LCAP register */
+ bool alt;
+ bool intc;
+ bool ofls;
+ bool lss;
+ int slcount;
+ int elid;
+ int elver;
+ u32 leptr;
+
+ struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
+
+ /* internal values computed from LCAP contents */
+ void __iomem *base_ptr;
+ u32 instance_offset;
+ u32 shim_offset;
+ u32 ip_offset;
+ u32 shim_vs_offset;
+};
+
+#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
+
+#define AZX_REG_SDW_INSTANCE_OFFSET 0x8000
+#define AZX_REG_SDW_SHIM_OFFSET 0x0
+#define AZX_REG_SDW_IP_OFFSET 0x100
+#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
+
+/* only one instance supported */
+#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
+#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
+
+#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
+#define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
+#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
+
+/* only one instance supported */
+#define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
+#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
+
+/* HDAML section - this part follows sequences in the hardware specification,
+ * including naming conventions and the use of the hdaml_ prefix.
+ * The code is intentionally minimal with limited dependencies on frameworks or
+ * helpers. Locking and scanning lists is handled at a higher level
+ */
+
+static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
+ void __iomem *ml_addr, int link_idx)
+{
+ struct hdac_ext_link *hlink = &h2link->hext_link;
+ u32 base_offset;
+
+ hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
+
+ h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
+
+ /* handle alternate extensions */
+ if (!h2link->alt) {
+ h2link->slcount = 1;
+
+ /*
+ * LSDIID is initialized by hardware for HDaudio link,
+ * it needs to be setup by software for alternate links
+ */
+ hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
+
+ dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
+ link_idx, hlink->lsdiid);
+
+ return 0;
+ }
+
+ h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
+ h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
+ h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
+
+ /* read slcount (increment due to zero-based hardware representation */
+ h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+ dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
+ link_idx, h2link->slcount);
+
+ /* find IP ID and offsets */
+ h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+
+ h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
+
+ base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
+ h2link->base_ptr = hlink->ml_addr + base_offset;
+
+ switch (h2link->elid) {
+ case AZX_REG_ML_LEPTR_ID_SDW:
+ h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
+ h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
+ h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
+ h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ default:
+ dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
+ link_idx, h2link->elid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Hardware recommendations are to wait ~10us before checking any hardware transition
+ * reported by bits changing status.
+ * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
+ * The worst-case is about 1ms before reporting an issue
+ */
+#define HDAML_POLL_DELAY_MIN_US 10
+#define HDAML_POLL_DELAY_SLACK_US 5
+#define HDAML_POLL_DELAY_RETRY 100
+
+static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
{
- if (bus->mlcap)
- snd_hdac_ext_bus_get_ml_capabilities(bus);
+ int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
+ int retry = HDAML_POLL_DELAY_RETRY;
+ u32 val;
+
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ do {
+ val = readl(lctl);
+ if (enabled) {
+ if (val & mask)
+ return 0;
+ } else {
+ if (!(val & mask))
+ return 0;
+ }
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+
+ } while (--retry);
+
+ return -EIO;
}
-void hda_bus_ml_free(struct hdac_bus *bus)
+static int hdaml_link_init(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+
+ val = readl(lctl);
+ val |= mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, true);
+}
+
+static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask;
+
+ val = readl(lctl);
+ mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+ val &= ~mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, false);
+}
+
+static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
+{
+ u32 val;
+
+ val = readl(lctl);
+ if (enable)
+ val |= AZX_ML_LCTL_INTEN;
+ else
+ val &= ~AZX_ML_LCTL_INTEN;
+
+ writel(val, lctl);
+}
+
+static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
+{
+ u32 val;
+
+ val = readl(lctl);
+
+ return val & AZX_ML_LCTL_INTSTS;
+}
+
+static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
+{
+ int timeout = HDAML_POLL_DELAY_RETRY;
+ u32 reg_read;
+
+ do {
+ reg_read = readl(base + offset);
+ if ((reg_read & mask) == target)
+ return 0;
+
+ timeout--;
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ } while (timeout != 0);
+
+ return -EAGAIN;
+}
+
+static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
+ val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
+
+ /*
+ * set SYNCPU but do not wait. The bit is cleared by hardware when
+ * the link becomes active.
+ */
+ val |= AZX_REG_ML_LSYNC_SYNCPU;
+
+ writel(val, lsync);
+}
+
+static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
+{
+ return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
+}
+
+static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
+
+ writel(val, lsync);
+}
+
+static void hdaml_link_sync_go(u32 __iomem *lsync)
{
+ u32 val;
+
+ val = readl(lsync);
+ val |= AZX_REG_ML_LSYNC_SYNCGO;
+
+ writel(val, lsync);
+}
+
+static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
+{
+ u32 val;
+
+ val = readl(lsync);
+
+ return !!(val & cmdsync_mask);
+}
+
+static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
+{
+ u32 val;
+
+ val = readl(lsdiid);
+ val |= BIT(dev_num);
+
+ writel(val, lsdiid);
+}
+
+static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
+{
+ u32 val = readl(lctl);
+
+ if (enable)
+ val |= AZX_ML_LCTL_OFLEN;
+ else
+ val &= ~AZX_ML_LCTL_OFLEN;
+
+ writel(val, lctl);
+}
+
+/* END HDAML section */
+
+static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
+{
+ struct hdac_ext2_link *h2link;
struct hdac_ext_link *hlink;
+ int ret;
+
+ h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
+ if (!h2link)
+ return -ENOMEM;
+
+ /* basic initialization */
+ hlink = &h2link->hext_link;
+
+ hlink->index = index;
+ hlink->bus = bus;
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
+
+ ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
+ if (ret < 0) {
+ kfree(h2link);
+ return ret;
+ }
+
+ mutex_init(&h2link->eml_lock);
+
+ list_add_tail(&hlink->list, &bus->hlink_list);
+
+ /*
+ * HDaudio regular links are powered-on by default, the
+ * refcount needs to be initialized.
+ */
+ if (!h2link->alt)
+ hlink->ref_count = 1;
+
+ return 0;
+}
+
+int hda_bus_ml_init(struct hdac_bus *bus)
+{
+ u32 link_count;
+ int ret;
+ int i;
+
+ if (!bus->mlcap)
+ return 0;
+
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
+
+ for (i = 0; i < link_count; i++) {
+ ret = hda_ml_alloc_h2link(bus, i);
+ if (ret < 0) {
+ hda_bus_ml_free(bus);
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
+
+void hda_bus_ml_free(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink, *_h;
+ struct hdac_ext2_link *h2link;
if (!bus->mlcap)
return;
- while (!list_empty(&bus->hlink_list)) {
- hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+ list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
list_del(&hlink->list);
- kfree(hlink);
+ h2link = hdac_ext_link_to_ext2(hlink);
+
+ mutex_destroy(&h2link->eml_lock);
+ kfree(h2link);
+ }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
+
+static struct hdac_ext2_link *
+find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (h2link->alt == alt && h2link->elid == elid)
+ return h2link;
}
+
+ return NULL;
+}
+
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ return h2link->slcount;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_enable_interrupt(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;
+
+ if (!h2link->intc)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ if (!h2link->intc)
+ return false;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
+{
+ return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->lss)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
+{
+ hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ u32 cmdsync_mask;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
+ AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
+
+ return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
+ cmdsync_mask);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (++hlink->ref_count > 1)
+ goto skip_init;
+
+ ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_init:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (--hlink->ref_count > 0)
+ goto skip_shutdown;
+
+ ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_shutdown:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return -ENODEV;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_put_all(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink;
- list_for_each_entry(hlink, &bus->hlink_list, list)
- snd_hdac_ext_bus_link_put(bus, hlink);
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+ }
}
+EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
{
@@ -64,6 +761,7 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
list_for_each_entry(hlink, &bus->hlink_list, list)
writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
}
+EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_resume(struct hdac_bus *bus)
{
@@ -72,7 +770,9 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
/* power up links that were active before suspend */
list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (hlink->ref_count) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt && hlink->ref_count) {
ret = snd_hdac_ext_bus_link_power_up(hlink);
if (ret < 0)
return ret;
@@ -80,10 +780,86 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
}
return 0;
}
+EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_suspend(struct hdac_bus *bus)
{
- return snd_hdac_ext_bus_link_power_down_all(bus);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
+
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->eml_lock;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+int 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;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
#endif
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index dc0b359ed9b6..981e7b699bdb 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -101,18 +101,23 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_dma_buffer *dmab;
int ret;
- u32 size, rate, bits;
-
- size = params_buffer_bytes(params);
- rate = hda_dsp_get_mult_div(sdev, params_rate(params));
- bits = hda_dsp_get_bits(sdev, params_width(params));
hstream->substream = substream;
dmab = substream->runtime->dma_buffer_p;
- hstream->format_val = rate | bits | (params_channels(params) - 1);
- hstream->bufsize = size;
+ /*
+ * Use the codec required format val (which is link_bps adjusted) when
+ * the DSP is not in use
+ */
+ if (!sdev->dspless_mode_selected) {
+ u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params));
+ u32 bits = hda_dsp_get_bits(sdev, params_width(params));
+
+ hstream->format_val = rate | bits | (params_channels(params) - 1);
+ }
+
+ hstream->bufsize = params_buffer_bytes(params);
hstream->period_bytes = params_period_bytes(params);
hstream->no_period_wakeup =
(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
@@ -249,6 +254,11 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
+ /* Only S16 and S32 supported by HDA hardware when used without DSP */
+ if (sdev->dspless_mode_selected)
+ snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);
+
/* binding pcm substream to hda stream */
substream->runtime->private_data = &dsp_stream->hstream;
return 0;
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 7f0fd05a96e6..8de422604ad5 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -182,6 +182,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream = NULL;
@@ -220,12 +222,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
/*
* Prevent DMI Link L1 entry for streams that don't support it.
* Workaround to address a known issue with host DMA that results
- * in xruns during pause/release in capture scenarios.
+ * in xruns during pause/release in capture scenarios. This is not needed for the ACE IP.
*/
- if (!(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE))
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 &&
+ !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
+ hda->l1_disabled = true;
+ }
return hext_stream;
}
@@ -233,6 +238,8 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
/* free a stream */
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream;
@@ -264,9 +271,11 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
spin_unlock_irq(&bus->reg_lock);
/* Enable DMI L1 if permitted */
- if (dmi_l1_enable)
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && dmi_l1_enable) {
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
+ hda->l1_disabled = false;
+ }
if (!found) {
dev_err(sdev->dev, "%s: stream_tag %d not opened!\n",
@@ -328,7 +337,13 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
/* cmd must be for audio stream */
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
+ if (hstream->running)
+ break;
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index,
1 << hstream->index);
@@ -351,8 +366,11 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
hstream->running = true;
break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset,
@@ -476,9 +494,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
{
const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *hstream = &hext_stream->hstream;
- int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret;
+ struct hdac_stream *hstream;
+ int sd_offset, ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
u32 mask;
u32 run;
@@ -493,10 +510,14 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
return -ENODEV;
}
- /* decouple host and link DMA */
- mask = 0x1 << hstream->index;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- mask, mask);
+ hstream = &hext_stream->hstream;
+ sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ mask = BIT(hstream->index);
+
+ /* decouple host and link DMA if the DSP is used */
+ if (!sdev->dspless_mode_selected)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ mask, mask);
/* clear stream status */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
@@ -597,11 +618,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
* enable decoupled mode
*/
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
/* couple host and link DMA, disable DSP features */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
mask, 0);
- }
/* program stream format */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -609,11 +629,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
SOF_HDA_ADSP_REG_SD_FORMAT,
0xffff, hstream->format_val);
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
/* decouple host and link DMA, enable DSP features */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
mask, mask);
- }
/* program last valid index */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -666,20 +685,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct hdac_ext_stream *hext_stream = container_of(hstream,
struct hdac_ext_stream,
hstream);
- struct hdac_bus *bus = sof_to_bus(sdev);
- u32 mask = 0x1 << hstream->index;
int ret;
ret = hda_dsp_stream_reset(sdev, hstream);
if (ret < 0)
return ret;
- spin_lock_irq(&bus->reg_lock);
- /* couple host and link DMA if link DMA channel is idle */
- if (!hext_stream->link_locked)
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
- SOF_HDA_REG_PP_PPCTL, mask, 0);
- spin_unlock_irq(&bus->reg_lock);
+ if (!sdev->dspless_mode_selected) {
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u32 mask = BIT(hstream->index);
+
+ spin_lock_irq(&bus->reg_lock);
+ /* couple host and link DMA if link DMA channel is idle */
+ if (!hext_stream->link_locked)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
+ SOF_HDA_REG_PP_PPCTL, mask, 0);
+ spin_unlock_irq(&bus->reg_lock);
+ }
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
@@ -861,12 +883,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
- hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+ if (sdev->bar[HDA_DSP_PP_BAR]) {
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
- SOF_HDA_PPLC_INTERVAL * i;
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+ SOF_HDA_PPLC_INTERVAL * i;
+ }
hstream = &hext_stream->hstream;
@@ -917,13 +941,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
- /* we always have DSP support */
- hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+ if (sdev->bar[HDA_DSP_PP_BAR]) {
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
- SOF_HDA_PPLC_INTERVAL * i;
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+ SOF_HDA_PPLC_INTERVAL * i;
+ }
hstream = &hext_stream->hstream;
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 81c697e20108..3153e21f100a 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -26,6 +26,7 @@
#include <sound/intel-nhlt.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
+#include <sound/hda-mlink.h>
#include "../sof-audio.h"
#include "../sof-pci-dev.h"
#include "../ops.h"
@@ -44,68 +45,37 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data)
+static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- struct snd_sof_dai *sof_dai = swidget->private;
- int ret;
-
- if (!sof_dai) {
- dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name);
- return -EINVAL;
- }
-
- if (tplg_ops->dai_config) {
- unsigned int flags;
-
- /* set HW_PARAMS flag along with quirks */
- flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
-
- ret = tplg_ops->dai_config(sdev, swidget, flags, data);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
- w->name);
- return ret;
- }
- }
-
- return 0;
-}
-
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- struct snd_sof_dai *sof_dai = swidget->private;
-
- if (!sof_dai) {
- dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name);
- return -EINVAL;
- }
-
- if (tplg_ops->dai_config) {
- unsigned int flags;
- int ret;
-
- /* set HW_FREE flag along with any quirks */
- flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ const struct sof_intel_dsp_desc *chip;
+ u32 interface_mask[2] = { 0 };
- ret = tplg_ops->dai_config(sdev, swidget, flags, data);
- if (ret < 0)
- dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__,
- w->name);
+ chip = get_chip_info(sdev->pdata);
+ switch (chip->hw_ip_version) {
+ case SOF_INTEL_TANGIER:
+ case SOF_INTEL_BAYTRAIL:
+ case SOF_INTEL_BROADWELL:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP);
+ break;
+ case SOF_INTEL_CAVS_1_5:
+ case SOF_INTEL_CAVS_1_5_PLUS:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ break;
+ case SOF_INTEL_CAVS_1_8:
+ case SOF_INTEL_CAVS_2_0:
+ case SOF_INTEL_CAVS_2_5:
+ case SOF_INTEL_ACE_1_0:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ break;
+ default:
+ break;
}
- return 0;
+ return interface_mask[sdev->dspless_mode_selected];
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
@@ -124,35 +94,17 @@ static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
struct snd_soc_dai *d = params_data->dai;
- struct snd_sof_dai_config_data data;
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->stream);
+ struct snd_sof_dai_config_data data = { 0 };
- w = snd_soc_dai_get_widget(d, params_data->stream);
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
-}
-
-static int sdw_free_stream(struct device *dev,
- struct sdw_intel_stream_free_data *free_data)
-{
- struct snd_soc_dai *d = free_data->dai;
- struct snd_sof_dai_config_data data;
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(d, free_data->stream);
- data.dai_index = (free_data->link_id << 8) | d->id;
-
- /* send invalid stream_id */
- data.dai_data = 0xFFFF;
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
}
struct sdw_intel_ops sdw_callback = {
.params_stream = sdw_params_stream,
- .free_stream = sdw_free_stream,
};
void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
@@ -171,8 +123,12 @@ void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return;
+
chip = get_chip_info(sdev->pdata);
if (chip && chip->enable_sdw_irq)
chip->enable_sdw_irq(sdev, enable);
@@ -180,10 +136,14 @@ void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
acpi_handle handle;
int ret;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return -EINVAL;
+
handle = ACPI_HANDLE(sdev->dev);
/* save ACPI info for the probe step */
@@ -254,8 +214,8 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
/* Check HW supported vs property value */
if (caps < ctx->count) {
dev_err(sdev->dev,
- "BIOS master count %d is larger than hardware capabilities %d\n",
- ctx->count, caps);
+ "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+ __func__, ctx->count, caps);
return -EINVAL;
}
@@ -337,8 +297,12 @@ out:
static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return false;
+
chip = get_chip_info(sdev->pdata);
if (chip && chip->check_sdw_irq)
return chip->check_sdw_irq(sdev);
@@ -353,8 +317,12 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return false;
+
hdev = sdev->pdata->hw_pdata;
if (hdev->sdw &&
snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@@ -366,8 +334,12 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return;
+
hdev = sdev->pdata->hw_pdata;
if (!hdev->sdw)
return;
@@ -927,6 +899,7 @@ static int dmic_detect_topology_fixup(struct snd_sof_dev *sdev,
static int hda_init_caps(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_sof_pdata *pdata = sdev->pdata;
struct sof_intel_hda_dev *hdev = pdata->hw_pdata;
@@ -945,7 +918,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return ret;
}
- hda_bus_ml_get_capabilities(bus);
+ hda_bus_ml_init(bus);
+
+ /* Skip SoundWire if it is not supported */
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ goto skip_soundwire;
/* scan SoundWire capabilities exposed by DSDT */
ret = hda_sdw_acpi_scan(sdev);
@@ -1054,21 +1031,25 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip;
int ret = 0;
- /*
- * detect DSP by checking class/subclass/prog-id information
- * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
- * class=04 subclass 01 prog-if 00: DSP is present
- * (and may be required e.g. for DMIC or SSP support)
- * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
- */
- if (pci->class == 0x040300) {
- dev_err(sdev->dev, "error: the DSP is not enabled on this platform, aborting probe\n");
- return -ENODEV;
- } else if (pci->class != 0x040100 && pci->class != 0x040380) {
- dev_err(sdev->dev, "error: unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n", pci->class);
- return -ENODEV;
+ if (!sdev->dspless_mode_selected) {
+ /*
+ * detect DSP by checking class/subclass/prog-id information
+ * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
+ * class=04 subclass 01 prog-if 00: DSP is present
+ * (and may be required e.g. for DMIC or SSP support)
+ * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
+ */
+ if (pci->class == 0x040300) {
+ dev_err(sdev->dev, "the DSP is not enabled on this platform, aborting probe\n");
+ return -ENODEV;
+ } else if (pci->class != 0x040100 && pci->class != 0x040380) {
+ dev_err(sdev->dev, "unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n",
+ pci->class);
+ return -ENODEV;
+ }
+ dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n",
+ pci->class);
}
- dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", pci->class);
chip = get_chip_info(sdev->pdata);
if (!chip) {
@@ -1104,12 +1085,18 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0;
#endif
+ if (sdev->dspless_mode_selected)
+ hdev->no_ipc_position = 1;
+
/* set up HDA base */
bus = sof_to_bus(sdev);
ret = hda_init(sdev);
if (ret < 0)
goto hdac_bus_unmap;
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp_setup;
+
/* DSP base */
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
if (!sdev->bar[HDA_DSP_BAR]) {
@@ -1120,6 +1107,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
sdev->mmio_bar = HDA_DSP_BAR;
sdev->mailbox_bar = HDA_DSP_BAR;
+skip_dsp_setup:
/* allow 64bit DMA address if supported by H/W */
if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(64))) {
@@ -1185,14 +1173,16 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto free_ipc_irq;
- /* enable ppcap interrupt */
- hda_dsp_ctrl_ppcap_enable(sdev, true);
- hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ if (!sdev->dspless_mode_selected) {
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
- INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ }
init_waitqueue_head(&hdev->waitq);
@@ -1208,7 +1198,8 @@ free_irq_vector:
free_streams:
hda_dsp_stream_free(sdev);
/* dsp_unmap: not currently used */
- iounmap(sdev->bar[HDA_DSP_BAR]);
+ if (!sdev->dspless_mode_selected)
+ iounmap(sdev->bar[HDA_DSP_BAR]);
hdac_bus_unmap:
platform_device_unregister(hdev->dmic_dev);
iounmap(bus->remap_addr);
@@ -1228,8 +1219,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (nhlt)
intel_nhlt_free(nhlt);
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected)
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
hda_codec_device_remove(sdev);
@@ -1238,14 +1230,19 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (!IS_ERR_OR_NULL(hda->dmic_dev))
platform_device_unregister(hda->dmic_dev);
- /* disable DSP IRQ */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_PIE, 0);
+ if (!sdev->dspless_mode_selected) {
+ /* disable DSP IRQ */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ SOF_HDA_PPCTL_PIE, 0);
+ }
/* disable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0);
+ if (sdev->dspless_mode_selected)
+ goto skip_disable_dsp;
+
/* no need to check for error as the DSP will be disabled anyway */
if (chip && chip->power_down_dsp)
chip->power_down_dsp(sdev);
@@ -1254,6 +1251,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_GPROCEN, 0);
+skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);
@@ -1262,7 +1260,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
hda_bus_ml_free(sof_to_bus(sdev));
- iounmap(sdev->bar[HDA_DSP_BAR]);
+ if (!sdev->dspless_mode_selected)
+ iounmap(sdev->bar[HDA_DSP_BAR]);
+
iounmap(bus->remap_addr);
sof_hda_bus_exit(sdev);
@@ -1568,12 +1568,16 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
- struct snd_soc_acpi_mach *mach;
+ struct snd_soc_acpi_mach *mach = NULL;
const char *tplg_filename;
- mach = snd_soc_acpi_find_machine(desc->machines);
+ /* Try I2S or DMIC if it is supported */
+ if (interface_mask & (BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC)))
+ mach = snd_soc_acpi_find_machine(desc->machines);
+
if (mach) {
bool add_extension = false;
bool tplg_fixup = false;
@@ -1680,10 +1684,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
}
}
- /*
- * If I2S fails, try SoundWire
- */
- if (!mach)
+ /* If I2S fails, try SoundWire if it is supported */
+ if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
mach = hda_sdw_machine_select(sdev);
/*
@@ -1729,3 +1731,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
MODULE_IMPORT_NS(SOUNDWIRE_INTEL);
+MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 45f9d4248f14..c4befacde23e 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -502,7 +502,7 @@ struct sof_intel_hda_dev {
u32 stream_max;
/* PM related */
- bool l1_support_changed;/* during suspend, is L1SEN changed or not */
+ bool l1_disabled;/* is DMI link L1 disabled? */
/* DMIC device */
struct platform_device *dmic_dev;
@@ -584,8 +584,10 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state);
int hda_dsp_resume(struct snd_sof_dev *sdev);
@@ -763,26 +765,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus);
-void hda_bus_ml_free(struct hdac_bus *bus);
-void hda_bus_ml_put_all(struct hdac_bus *bus);
-void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
-int hda_bus_ml_resume(struct hdac_bus *bus);
-int hda_bus_ml_suspend(struct hdac_bus *bus);
-
-#else
-
-static inline void hda_bus_ml_get_capabilities(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
-static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
-static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
-
-#endif /* CONFIG_SND_SOC_SOF_HDA */
-
/*
* Trace Control.
*/
@@ -896,10 +878,6 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_sof_dai;
struct sof_ipc_dai_config;
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data);
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data);
#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
@@ -928,4 +906,41 @@ extern struct sdw_intel_ops sdw_callback;
struct sof_ipc4_fw_library;
int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
+
+/**
+ * struct hda_dai_widget_dma_ops - DAI DMA ops optional by default unless specified otherwise
+ * @get_hext_stream: Mandatory function pointer to get the saved pointer to struct hdac_ext_stream
+ * @assign_hext_stream: Function pointer to assign a hdac_ext_stream
+ * @release_hext_stream: Function pointer to release the hdac_ext_stream
+ * @setup_hext_stream: Function pointer for hdac_ext_stream setup
+ * @reset_hext_stream: Function pointer for hdac_ext_stream reset
+ * @pre_trigger: Function pointer for DAI DMA pre-trigger actions
+ * @trigger: Function pointer for DAI DMA trigger actions
+ * @post_trigger: Function pointer for DAI DMA post-trigger actions
+ */
+struct hda_dai_widget_dma_ops {
+ struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val);
+ void (*reset_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_sream);
+ int (*pre_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+};
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data);
+
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 435941a1692f..0f249efc6a5a 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -116,6 +116,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_icl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -141,6 +143,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_icl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* debug */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 307faad2ecf4..46caf3ccde66 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -60,6 +60,9 @@ static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
u32 irq_status;
u32 hfintipptr;
+ if (sdev->dspless_mode_selected)
+ return false;
+
/* read Interrupt IP Pointer */
hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, hfintipptr + MTL_DSP_IRQSTS);
@@ -120,6 +123,9 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* enable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE,
@@ -131,6 +137,9 @@ static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* disable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE, 0);
@@ -143,6 +152,9 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
u32 val;
int ret;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* Enable/Disable SoundWire interrupt */
mask = MTL_DSP_REG_HfSNDWIE_IE_MASK;
if (enable)
@@ -170,6 +182,9 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
u32 val;
int ret;
+ if (sdev->dspless_mode_selected)
+ return 0;
+
/* read Interrupt IP Pointer */
hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
@@ -217,6 +232,7 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
/* pre fw run operations */
static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
u32 dsphfpwrsts;
u32 dsphfdsscs;
u32 cpa;
@@ -255,9 +271,11 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
if (ret < 0)
dev_err(sdev->dev, "failed to power up gated DSP domain\n");
- /* make sure SoundWire is not power-gated */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, MTL_HFPWRCTL,
- MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+ /* if SoundWire is used, make sure it is not power-gated */
+ if (hdev->info.handle && hdev->info.link_mask > 0)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFPWRCTL,
+ MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+
return ret;
}
@@ -650,6 +668,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
/* set DAI ops */
hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
+ sof_mtl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+
return 0;
};
EXPORT_SYMBOL_NS(sof_mtl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index aff6cb573c27..69cad5a6bc72 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -29,6 +29,7 @@ static const struct sof_dev_desc bxt_desc = {
.chip_info = &apl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/apl",
@@ -60,6 +61,7 @@ static const struct sof_dev_desc glk_desc = {
.chip_info = &apl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/glk",
diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c
index 4c0c1c369dcd..8895508a0be6 100644
--- a/sound/soc/sof/intel/pci-cnl.c
+++ b/sound/soc/sof/intel/pci-cnl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc cnl_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc cfl_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -94,6 +96,7 @@ static const struct sof_dev_desc cml_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c
index 6785669113b3..5fb5a820693e 100644
--- a/sound/soc/sof/intel/pci-icl.c
+++ b/sound/soc/sof/intel/pci-icl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc icl_desc = {
.chip_info = &icl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/icl",
@@ -61,6 +62,7 @@ static const struct sof_dev_desc jsl_desc = {
.chip_info = &jsl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/jsl",
diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c
index b183dc0014b4..e276e1e37fed 100644
--- a/sound/soc/sof/intel/pci-mtl.c
+++ b/sound/soc/sof/intel/pci-mtl.c
@@ -31,6 +31,7 @@ static const struct sof_dev_desc mtl_desc = {
.chip_info = &mtl_chip_info,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl",
},
diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c
index 5b4bccf81965..5e69af6eed34 100644
--- a/sound/soc/sof/intel/pci-skl.c
+++ b/sound/soc/sof/intel/pci-skl.c
@@ -26,6 +26,7 @@ static struct sof_dev_desc skl_desc = {
.irqindex_host_ipc = -1,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/avs/skl",
},
@@ -50,6 +51,7 @@ static struct sof_dev_desc kbl_desc = {
.irqindex_host_ipc = -1,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/avs/kbl",
},
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index 22e769e0831d..ca37ff1bbd2a 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc tgl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/tgl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc tglh_desc = {
.chip_info = &tglh_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/tgl-h",
@@ -93,6 +95,7 @@ static const struct sof_dev_desc ehl_desc = {
.chip_info = &ehl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/ehl",
@@ -125,6 +128,7 @@ static const struct sof_dev_desc adls_desc = {
.chip_info = &adls_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl-s",
@@ -157,6 +161,7 @@ static const struct sof_dev_desc adl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl",
@@ -189,6 +194,7 @@ static const struct sof_dev_desc adl_n_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl-n",
@@ -221,6 +227,7 @@ static const struct sof_dev_desc rpls_desc = {
.chip_info = &adls_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/rpl-s",
@@ -253,6 +260,7 @@ static const struct sof_dev_desc rpl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/rpl",
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 58ac3a46e6a7..2713b7dc7931 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -71,6 +71,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_tgl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -96,6 +98,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_tgl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index 217ac5501a98..ad040e7bb850 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -104,7 +104,7 @@ unlock:
return ret;
}
-static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
+static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol)
{
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
@@ -138,7 +138,7 @@ static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,
unsigned int channels = scontrol->num_channels;
unsigned int i;
- snd_sof_refresh_control(scontrol);
+ sof_ipc3_refresh_control(scontrol);
/* read back each channel */
for (i = 0; i < channels; i++)
@@ -189,7 +189,7 @@ static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,
unsigned int channels = scontrol->num_channels;
unsigned int i;
- snd_sof_refresh_control(scontrol);
+ sof_ipc3_refresh_control(scontrol);
/* read back each channel */
for (i = 0; i < channels; i++)
@@ -237,7 +237,7 @@ static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,
unsigned int channels = scontrol->num_channels;
unsigned int i;
- snd_sof_refresh_control(scontrol);
+ sof_ipc3_refresh_control(scontrol);
/* read back each channel */
for (i = 0; i < channels; i++)
@@ -286,7 +286,7 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
struct sof_abi_hdr *data = cdata->data;
size_t size;
- snd_sof_refresh_control(scontrol);
+ sof_ipc3_refresh_control(scontrol);
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
@@ -343,55 +343,6 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
return 0;
}
-static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
- const unsigned int __user *binary_data, unsigned int size)
-{
- struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
- struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
- struct snd_soc_component *scomp = scontrol->scomp;
- struct snd_ctl_tlv header;
- size_t data_size;
-
- snd_sof_refresh_control(scontrol);
-
- /*
- * Decrement the limit by ext bytes header size to
- * ensure the user space buffer is not exceeded.
- */
- if (size < sizeof(struct snd_ctl_tlv))
- return -ENOSPC;
-
- size -= sizeof(struct snd_ctl_tlv);
-
- /* set the ABI header values */
- cdata->data->magic = SOF_ABI_MAGIC;
- cdata->data->abi = SOF_ABI_VERSION;
-
- /* check data size doesn't exceed max coming from topology */
- if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
- dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
- cdata->data->size,
- scontrol->max_size - sizeof(struct sof_abi_hdr));
- return -EINVAL;
- }
-
- data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
-
- /* make sure we don't exceed size provided by user space for data */
- if (data_size > size)
- return -ENOSPC;
-
- header.numid = cdata->cmd;
- header.length = data_size;
- if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
- return -EFAULT;
-
- if (copy_to_user(tlvd->tlv, cdata->data, data_size))
- return -EFAULT;
-
- return 0;
-}
-
static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
const unsigned int __user *binary_data,
unsigned int size)
@@ -457,16 +408,15 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
return 0;
}
-static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
- const unsigned int __user *binary_data,
- unsigned int size)
+static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size, bool from_dsp)
{
struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_ctl_tlv header;
size_t data_size;
- int ret;
/*
* Decrement the limit by ext bytes header size to
@@ -482,9 +432,12 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
- if (ret < 0)
- return ret;
+ if (from_dsp) {
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
+
+ if (ret < 0)
+ return ret;
+ }
/* check data size doesn't exceed max coming from topology */
if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
@@ -508,7 +461,20 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
if (copy_to_user(tlvd->tlv, cdata->data, data_size))
return -EFAULT;
- return ret;
+ return 0;
+}
+
+static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data, unsigned int size)
+{
+ return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, false);
+}
+
+static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, true);
}
static void snd_sof_update_control(struct snd_sof_control *scontrol,
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index b815b0244d9e..1d3bca2d28dd 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -150,7 +150,6 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
struct sof_ipc_trace_filter_elem *elems)
{
struct sof_ipc_trace_filter *msg;
- struct sof_ipc_reply reply;
size_t size;
int ret;
@@ -172,13 +171,13 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
dev_err(sdev->dev, "enabling device failed: %d\n", ret);
goto error;
}
- ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
pm_runtime_mark_last_busy(sdev->dev);
pm_runtime_put_autosuspend(sdev->dev);
error:
kfree(msg);
- return ret ? ret : reply.error;
+ return ret;
}
static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
@@ -434,7 +433,6 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_dma_trace_params_ext params;
- struct sof_ipc_reply ipc_reply;
int ret;
if (!sdev->fw_trace_is_supported)
@@ -474,7 +472,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
/* send IPC to the DSP */
priv->dtrace_state = SOF_DTRACE_INITIALIZING;
- ret = sof_ipc_tx_message(sdev->ipc, &params, sizeof(params), &ipc_reply, sizeof(ipc_reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &params, sizeof(params));
if (ret < 0) {
dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
goto trace_release;
@@ -604,7 +602,6 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_cmd_hdr hdr;
- struct sof_ipc_reply ipc_reply;
int ret;
if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
@@ -623,8 +620,7 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
hdr.size = sizeof(hdr);
hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
- ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
- &ipc_reply, sizeof(ipc_reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &hdr, hdr.size);
if (ret < 0)
dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
}
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index b29d93e0d216..304faf6425ab 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -19,7 +19,6 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
struct snd_sof_pcm *spcm;
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -34,7 +33,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
stream.comp_id = spcm->stream[substream->stream].comp_id;
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
}
static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
@@ -146,7 +145,6 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
struct snd_sof_pcm *spcm;
spcm = snd_sof_find_spcm_dai(component, rtd);
@@ -178,7 +176,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
}
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
}
static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
@@ -382,4 +380,5 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
.hw_free = sof_ipc3_pcm_hw_free,
.trigger = sof_ipc3_pcm_trigger,
.dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
+ .reset_hw_params_during_stop = true,
};
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index b1f425b39db9..fc1eb8e2de2c 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -1627,7 +1627,6 @@ static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget)
static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
{
struct sof_ipc_pipe_comp_connect connect;
- struct sof_ipc_reply reply;
int ret;
connect.hdr.size = sizeof(connect);
@@ -1640,7 +1639,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
sroute->sink_widget->widget->name);
/* send ipc */
- ret = sof_ipc_tx_message(sdev->ipc, &connect, sizeof(connect), &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect));
if (ret < 0)
dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,
sroute->src_widget->widget->name, sroute->sink_widget->widget->name);
@@ -1789,7 +1788,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro
fcomp.id = scontrol->comp_id;
/* send IPC to the DSP */
- return sof_ipc_tx_message(sdev->ipc, &fcomp, sizeof(fcomp), NULL, 0);
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &fcomp, sizeof(fcomp));
}
/* send pcm params ipc */
@@ -1797,7 +1796,6 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
{
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_pcm_params_reply ipc_params_reply;
struct snd_pcm_hw_params *params;
struct sof_ipc_pcm_params pcm;
struct snd_sof_pcm *spcm;
@@ -1841,8 +1839,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in
}
/* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
- &ipc_params_reply, sizeof(ipc_params_reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm));
if (ret < 0)
dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,
swidget->widget->name);
@@ -1856,7 +1853,6 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc_stream stream;
- struct sof_ipc_reply reply;
int ret;
/* set IPC stream params */
@@ -1865,7 +1861,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c
stream.comp_id = swidget->comp_id;
/* send IPC to the DSP */
- ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
if (ret < 0)
dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);
@@ -1982,7 +1978,6 @@ static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp,
static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct sof_ipc_pipe_ready ready;
- struct sof_ipc_reply reply;
int ret;
dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
@@ -1993,7 +1988,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w
ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;
ready.comp_id = swidget->comp_id;
- ret = sof_ipc_tx_message(sdev->ipc, &ready, sizeof(ready), &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ready, sizeof(ready));
if (ret < 0)
return ret;
@@ -2009,7 +2004,6 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
},
.id = swidget->comp_id,
};
- struct sof_ipc_reply reply;
int ret;
if (!swidget->private)
@@ -2029,8 +2023,7 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
break;
}
- ret = sof_ipc_tx_message(sdev->ipc, &ipc_free, sizeof(ipc_free),
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free));
if (ret < 0)
dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);
@@ -2044,7 +2037,6 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
struct snd_sof_dai *dai = swidget->private;
struct sof_dai_private_data *private;
struct sof_ipc_dai_config *config;
- struct sof_ipc_reply reply;
int ret = 0;
if (!dai || !dai->private) {
@@ -2118,8 +2110,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
/* only send the IPC if the widget is set up in the DSP */
if (swidget->use_count > 0) {
- ret = sof_ipc_tx_message(sdev->ipc, config, config->hdr.size,
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size);
if (ret < 0)
dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);
@@ -2132,7 +2123,6 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- struct sof_ipc_comp_reply reply;
int ret;
if (!swidget->private)
@@ -2146,8 +2136,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_dai_private_data *dai_data = dai->private;
struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;
- ret = sof_ipc_tx_message(sdev->ipc, dai_data->comp_dai,
- comp->hdr.size, &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, dai_data->comp_dai, comp->hdr.size);
break;
}
case snd_soc_dapm_scheduler:
@@ -2155,8 +2144,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc_pipe_new *pipeline;
pipeline = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, pipeline, sizeof(*pipeline),
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, pipeline, sizeof(*pipeline));
break;
}
default:
@@ -2164,8 +2152,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc_cmd_hdr *hdr;
hdr = swidget->private;
- ret = sof_ipc_tx_message(sdev->ipc, swidget->private, hdr->size,
- &reply, sizeof(reply));
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, swidget->private, hdr->size);
break;
}
}
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 4493bbd7faf1..c67767742093 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -1044,15 +1044,13 @@ static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool
.hdr.size = sizeof(core_cfg),
.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
};
- struct sof_ipc_reply reply;
if (on)
core_cfg.enable_mask = sdev->enabled_cores_mask | BIT(core_idx);
else
core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx);
- return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg),
- &reply, sizeof(reply), false);
+ return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg), NULL, 0, false);
}
static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
@@ -1061,11 +1059,9 @@ static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
.hdr.size = sizeof(pm_ctx),
.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd,
};
- struct sof_ipc_reply reply;
/* send ctx save ipc to dsp */
- return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx),
- &reply, sizeof(reply), false);
+ return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx), NULL, 0, false);
}
static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
@@ -1081,7 +1077,6 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
{
struct sof_ipc_pm_gate pm_gate;
- struct sof_ipc_reply reply;
memset(&pm_gate, 0, sizeof(pm_gate));
@@ -1091,8 +1086,7 @@ static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
pm_gate.flags = flags;
/* send pm_gate ipc to dsp */
- return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
- &reply, sizeof(reply));
+ return sof_ipc_tx_message_no_pm_no_reply(sdev->ipc, &pm_gate, sizeof(pm_gate));
}
static const struct sof_ipc_pm_ops ipc3_pm_ops = {
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 9a71af1a613a..6f0698be9451 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -181,21 +181,263 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
return 0;
}
+static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
+ struct snd_sof_control *scontrol,
+ bool set, bool lock)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+ int ret = 0;
+
+ /* Send the new data to the firmware only if it is powered up */
+ if (set && !pm_runtime_active(sdev->dev))
+ return 0;
+
+ msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);
+
+ msg->data_ptr = data->data;
+ msg->data_size = data->size;
+
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);
+ if (ret < 0)
+ dev_err(sdev->dev, "Failed to %s for %s\n",
+ set ? "set bytes update" : "get bytes",
+ scontrol->name);
+
+ msg->data_ptr = NULL;
+ msg->data_size = 0;
+
+ return ret;
+}
+
+static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ 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);
+ struct sof_abi_hdr *data = cdata->data;
+ size_t size;
+
+ if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+ dev_err_ratelimited(scomp->dev,
+ "data max %zu exceeds ucontrol data array size\n",
+ scontrol->max_size);
+ return -EINVAL;
+ }
+
+ /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
+ if (data->size > scontrol->max_size - sizeof(*data)) {
+ dev_err_ratelimited(scomp->dev,
+ "data size too big %u bytes max is %zu\n",
+ data->size, scontrol->max_size - sizeof(*data));
+ return -EINVAL;
+ }
+
+ size = data->size + sizeof(*data);
+
+ /* copy from kcontrol */
+ memcpy(data, ucontrol->value.bytes.data, size);
+
+ sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+
+ return 0;
+}
+
+static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_abi_hdr *data = cdata->data;
+ size_t size;
+
+ if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
+ dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
+ scontrol->max_size);
+ return -EINVAL;
+ }
+
+ if (data->size > scontrol->max_size - sizeof(*data)) {
+ dev_err_ratelimited(scomp->dev,
+ "%u bytes of control data is invalid, max is %zu\n",
+ data->size, scontrol->max_size - sizeof(*data));
+ return -EINVAL;
+ }
+
+ size = data->size + sizeof(*data);
+
+ /* copy back to kcontrol */
+ memcpy(ucontrol->value.bytes.data, data, size);
+
+ return 0;
+}
+
+static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+ struct sof_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);
+ struct sof_abi_hdr *data = cdata->data;
+ struct sof_abi_hdr abi_hdr;
+ struct snd_ctl_tlv header;
+
+ /*
+ * The beginning of bytes data contains a header from where
+ * the length (as bytes) is needed to know the correct copy
+ * length of data from tlvd->tlv.
+ */
+ if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))
+ return -EFAULT;
+
+ /* make sure TLV info is consistent */
+ if (header.length + sizeof(struct snd_ctl_tlv) > size) {
+ dev_err_ratelimited(scomp->dev,
+ "Inconsistent TLV, data %d + header %zu > %d\n",
+ header.length, sizeof(struct snd_ctl_tlv), size);
+ return -EINVAL;
+ }
+
+ /* be->max is coming from topology */
+ if (header.length > scontrol->max_size) {
+ dev_err_ratelimited(scomp->dev,
+ "Bytes data size %d exceeds max %zu\n",
+ header.length, scontrol->max_size);
+ return -EINVAL;
+ }
+
+ /* Verify the ABI header first */
+ if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr)))
+ return -EFAULT;
+
+ if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) {
+ dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n",
+ abi_hdr.magic);
+ return -EINVAL;
+ }
+
+ if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "%u bytes of control data is invalid, max is %zu\n",
+ abi_hdr.size, scontrol->max_size - sizeof(abi_hdr));
+ return -EINVAL;
+ }
+
+ /* Copy the whole binary data which includes the ABI header and the payload */
+ if (copy_from_user(data, tlvd->tlv, header.length))
+ return -EFAULT;
+
+ sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);
+
+ return 0;
+}
+
+static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size, bool from_dsp)
+{
+ struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct snd_soc_component *scomp = scontrol->scomp;
+ struct sof_abi_hdr *data = cdata->data;
+ struct snd_ctl_tlv header;
+ size_t data_size;
+
+ /*
+ * Decrement the limit by ext bytes header size to ensure the user space
+ * buffer is not exceeded.
+ */
+ if (size < sizeof(struct snd_ctl_tlv))
+ return -ENOSPC;
+
+ size -= sizeof(struct snd_ctl_tlv);
+
+ /* get all the component data from DSP */
+ if (from_dsp) {
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true);
+
+ if (ret < 0)
+ return ret;
+
+ /* Set the ABI magic (if the control is not initialized) */
+ data->magic = SOF_IPC4_ABI_MAGIC;
+ }
+
+ if (data->size > scontrol->max_size - sizeof(*data)) {
+ dev_err_ratelimited(scomp->dev,
+ "%u bytes of control data is invalid, max is %zu\n",
+ data->size, scontrol->max_size - sizeof(*data));
+ return -EINVAL;
+ }
+
+ data_size = data->size + sizeof(struct sof_abi_hdr);
+
+ /* make sure we don't exceed size provided by user space for data */
+ if (data_size > size)
+ return -ENOSPC;
+
+ header.numid = scontrol->comp_id;
+ header.length = data_size;
+
+ if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
+ return -EFAULT;
+
+ if (copy_to_user(tlvd->tlv, data, data_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);
+}
+
+static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true);
+}
+
/* set up all controls for the widget */
static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct snd_sof_control *scontrol;
- int ret;
+ int ret = 0;
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
if (scontrol->comp_id == swidget->comp_id) {
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
+ switch (scontrol->info_type) {
+ case SND_SOC_TPLG_CTL_VOLSW:
+ case SND_SOC_TPLG_CTL_VOLSW_SX:
+ case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
+ ret = sof_ipc4_set_volume_data(sdev, swidget,
+ scontrol, false);
+ break;
+ case SND_SOC_TPLG_CTL_BYTES:
+ ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
+ true, false);
+ break;
+ default:
+ break;
+ }
+
if (ret < 0) {
- dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
- __func__, scontrol->comp_id, swidget->widget->name);
+ dev_err(sdev->dev,
+ "kcontrol %d set up failed for widget %s\n",
+ scontrol->comp_id, swidget->widget->name);
return ret;
}
}
+ }
return 0;
}
@@ -225,6 +467,11 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I
const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
.volume_put = sof_ipc4_volume_put,
.volume_get = sof_ipc4_volume_get,
+ .bytes_put = sof_ipc4_bytes_put,
+ .bytes_get = sof_ipc4_bytes_get,
+ .bytes_ext_put = sof_ipc4_bytes_ext_put,
+ .bytes_ext_get = sof_ipc4_bytes_ext_get,
+ .bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get,
.widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
.set_up_volume_table = sof_ipc4_set_up_volume_table,
};
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
index 0ec6ef681012..2b4659a1768e 100644
--- a/sound/soc/sof/ipc4-mtrace.c
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -609,6 +609,16 @@ static void ipc4_mtrace_free(struct snd_sof_dev *sdev)
ipc4_mtrace_disable(sdev);
}
+static int sof_ipc4_mtrace_update_pos_all_cores(struct snd_sof_dev *sdev)
+{
+ int i;
+
+ for (i = 0; i < sdev->num_cores; i++)
+ sof_ipc4_mtrace_update_pos(sdev, i);
+
+ return 0;
+}
+
int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core)
{
struct sof_mtrace_priv *priv = sdev->fw_trace_data;
@@ -642,6 +652,16 @@ int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core)
return 0;
}
+static void ipc4_mtrace_fw_crashed(struct snd_sof_dev *sdev)
+{
+ /*
+ * The DSP might not be able to send SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS
+ * messages anymore, so check the log buffer status on all
+ * cores and process any pending messages.
+ */
+ sof_ipc4_mtrace_update_pos_all_cores(sdev);
+}
+
static int ipc4_mtrace_resume(struct snd_sof_dev *sdev)
{
return ipc4_mtrace_enable(sdev);
@@ -655,6 +675,7 @@ static void ipc4_mtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops = {
.init = ipc4_mtrace_init,
.free = ipc4_mtrace_free,
+ .fw_crashed = ipc4_mtrace_fw_crashed,
.suspend = ipc4_mtrace_suspend,
.resume = ipc4_mtrace_resume,
};
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 68258767aace..9e2b6c45080d 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -39,7 +39,7 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state
msg.data_size = ipc_size;
msg.data_ptr = trigger_list;
- return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size);
}
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
@@ -57,7 +57,7 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
msg.primary = primary;
- return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
}
EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
@@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
* prepare ioctl before the START trigger.
*/
+/*
+ * Chained DMA is a special case where there is no processing on
+ * DSP. The samples are just moved over by host side DMA to a single
+ * buffer on DSP and directly from there to link DMA. However, the
+ * model on SOF driver has two notional pipelines, one at host DAI,
+ * and another at link DAI. They both shall have the use_chain_dma
+ * attribute.
+ */
+
+static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
+ int state, int cmd)
+{
+ bool allocate, enable, set_fifo_size;
+ struct sof_ipc4_msg msg = {{ 0 }};
+ int i;
+
+ switch (state) {
+ case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
+ allocate = true;
+ enable = true;
+ /*
+ * SOF assumes creation of a new stream from the presence of fifo_size
+ * in the message, so we must leave it out in pause release case.
+ */
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+ set_fifo_size = false;
+ else
+ set_fifo_size = true;
+ break;
+ case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */
+ allocate = true;
+ enable = false;
+ set_fifo_size = false;
+ break;
+ case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
+ allocate = false;
+ enable = false;
+ set_fifo_size = false;
+ break;
+ default:
+ dev_err(sdev->dev, "Unexpected state %d", state);
+ return -EINVAL;
+ }
+
+ msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+
+ /*
+ * To set-up the DMA chain, the host DMA ID and SCS setting
+ * are retrieved from the host pipeline configuration. Likewise
+ * the link DMA ID and fifo_size are retrieved from the link
+ * pipeline configuration.
+ */
+ for (i = 0; i < pipeline_list->count; i++) {
+ struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i];
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (!pipeline->use_chain_dma) {
+ dev_err(sdev->dev,
+ "All pipelines in chained DMA stream should have use_chain_dma attribute set.");
+ return -EINVAL;
+ }
+
+ msg.primary |= pipeline->msg.primary;
+
+ /* Add fifo_size (actually DMA buffer size) field to the message */
+ if (set_fifo_size)
+ msg.extension |= pipeline->msg.extension;
+ }
+
+ if (allocate)
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
+
+ if (enable)
+ msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
+
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+}
+
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int state, int cmd)
{
@@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct ipc4_pipeline_set_state_data *trigger_list;
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
struct snd_sof_pipeline *spipe;
struct snd_sof_pcm *spcm;
int ret;
@@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
if (!pipeline_list->pipelines || !pipeline_list->count)
return 0;
+ spipe = pipeline_list->pipelines[0];
+ pipe_widget = spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ /*
+ * If use_chain_dma attribute is set we proceed to chained DMA
+ * trigger function that handles the rest for the substream.
+ */
+ if (pipeline->use_chain_dma)
+ return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd);
+
/* allocate memory for the pipeline data */
trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
GFP_KERNEL);
@@ -362,15 +457,70 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const
}
}
+/*
+ * Fixup DAI link parameters for sampling rate based on
+ * DAI copier configuration.
+ */
+static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev,
+ struct snd_pcm_hw_params *params,
+ struct sof_ipc4_copier *ipc4_copier)
+{
+ struct sof_ipc4_pin_format *pin_fmts = ipc4_copier->available_fmt.input_pin_fmts;
+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ int num_input_formats = ipc4_copier->available_fmt.num_input_formats;
+ unsigned int fe_rate = params_rate(params);
+ bool fe_be_rate_match = false;
+ bool single_be_rate = true;
+ unsigned int be_rate;
+ int i;
+
+ /*
+ * Copier does not change sampling rate, so we
+ * need to only consider the input pin information.
+ */
+ for (i = 0; i < num_input_formats; i++) {
+ unsigned int val = pin_fmts[i].audio_fmt.sampling_frequency;
+
+ if (i == 0)
+ be_rate = val;
+ else if (val != be_rate)
+ single_be_rate = false;
+
+ if (val == fe_rate) {
+ fe_be_rate_match = true;
+ break;
+ }
+ }
+
+ /*
+ * If rate is different than FE rate, topology must
+ * contain an SRC. But we do require topology to
+ * define a single rate in the DAI copier config in
+ * this case (FE rate may be variable).
+ */
+ if (!fe_be_rate_match) {
+ if (!single_be_rate) {
+ dev_err(sdev->dev, "Unable to select sampling rate for DAI link\n");
+ return -EINVAL;
+ }
+
+ rate->min = be_rate;
+ rate->max = rate->min;
+ }
+
+ return 0;
+}
+
static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name);
- struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct sof_ipc4_copier *ipc4_copier;
+ bool use_chain_dma = false;
+ int dir;
if (!dai) {
dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -385,12 +535,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
return -EINVAL;
}
- /* always set BE format to 32-bits for both playback and capture */
- snd_mask_none(fmt);
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+ for_each_pcm_streams(dir) {
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir);
+
+ if (w) {
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->use_chain_dma)
+ use_chain_dma = true;
+ }
+ }
+
+ /* Chain DMA does not use copiers, so no fixup needed */
+ if (!use_chain_dma) {
+ int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier);
- rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
- rate->max = rate->min;
+ if (ret)
+ return ret;
+ }
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_SSP:
@@ -671,5 +835,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
.pcm_setup = sof_ipc4_pcm_setup,
.pcm_free = sof_ipc4_pcm_free,
- .delay = sof_ipc4_pcm_delay
+ .delay = sof_ipc4_pcm_delay,
+ .ipc_first_on_start = true,
+ .platform_stop_during_hw_free = true,
};
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index a623707c8ffc..059eebf0a687 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -6,6 +6,7 @@
// Copyright(c) 2022 Intel Corporation. All rights reserved.
//
//
+#include <linux/bitfield.h>
#include <uapi/sound/sof/tokens.h>
#include <sound/pcm_params.h>
#include <sound/sof/ext_manifest4.h>
@@ -18,13 +19,18 @@
#define SOF_IPC4_GAIN_PARAM_ID 0
#define SOF_IPC4_TPLG_ABI_SIZE 6
+#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
static DEFINE_IDA(alh_group_ida);
static DEFINE_IDA(pipeline_ida);
static const struct sof_topology_token ipc4_sched_tokens[] = {
{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_pipeline, lp_mode)}
+ offsetof(struct sof_ipc4_pipeline, lp_mode)},
+ {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
+ offsetof(struct sof_ipc4_pipeline, use_chain_dma)},
+ {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pipeline, core_id)},
};
static const struct sof_topology_token pipeline_tokens[] = {
@@ -39,45 +45,48 @@ static const struct sof_topology_token ipc4_comp_tokens[] = {
offsetof(struct sof_ipc4_base_module_cfg, is_pages)},
};
-static const struct sof_topology_token ipc4_audio_format_buffer_size_tokens[] = {
- {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_base_module_cfg, ibs)},
- {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_base_module_cfg, obs)},
-};
-
static const struct sof_topology_token ipc4_in_audio_format_tokens[] = {
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, bit_depth)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, ch_map)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, ch_cfg)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
+ get_token_u32, offsetof(struct sof_ipc4_pin_format,
+ audio_fmt.interleaving_style)},
{SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
+ {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pin_format, pin_index)},
+ {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pin_format, buffer_size)},
};
static const struct sof_topology_token ipc4_out_audio_format_tokens[] = {
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, bit_depth)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, ch_map)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, ch_cfg)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
- get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
+ get_token_u32, offsetof(struct sof_ipc4_pin_format,
+ audio_fmt.interleaving_style)},
{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
+ offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
+ {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pin_format, pin_index)},
+ {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_pin_format, buffer_size)},
};
-static const struct sof_topology_token ipc4_copier_gateway_cfg_tokens[] = {
- {SOF_TKN_CAVS_AUDIO_FORMAT_DMA_BUFFER_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
+static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = {
+ {SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
};
static const struct sof_topology_token ipc4_copier_tokens[] = {
@@ -85,8 +94,10 @@ static const struct sof_topology_token ipc4_copier_tokens[] = {
};
static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = {
- {SOF_TKN_COMP_NUM_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- 0},
+ {SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_available_audio_format, num_input_formats)},
+ {SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_available_audio_format, num_output_formats)},
};
static const struct sof_topology_token dai_tokens[] = {
@@ -100,6 +111,8 @@ static const struct sof_topology_token dai_tokens[] = {
static const struct sof_topology_token comp_ext_tokens[] = {
{SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
offsetof(struct snd_sof_widget, uuid)},
+ {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, core)},
};
static const struct sof_topology_token gain_tokens[] = {
@@ -131,11 +144,8 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)},
[SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens",
ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)},
- [SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS] = {"IPC4 Audio format buffer size tokens",
- ipc4_audio_format_buffer_size_tokens,
- ARRAY_SIZE(ipc4_audio_format_buffer_size_tokens)},
- [SOF_COPIER_GATEWAY_CFG_TOKENS] = {"IPC4 Copier gateway config tokens",
- ipc4_copier_gateway_cfg_tokens, ARRAY_SIZE(ipc4_copier_gateway_cfg_tokens)},
+ [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens",
+ ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)},
[SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens,
ARRAY_SIZE(ipc4_copier_tokens)},
[SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens",
@@ -144,21 +154,50 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
};
-static void sof_ipc4_dbg_audio_format(struct device *dev,
- struct sof_ipc4_audio_format *format,
- size_t object_size, int num_format)
+static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,
+ int num_formats)
{
- struct sof_ipc4_audio_format *fmt;
- void *ptr = format;
int i;
- for (i = 0; i < num_format; i++, ptr = (u8 *)ptr + object_size) {
- fmt = ptr;
+ for (i = 0; i < num_formats; i++) {
+ struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;
dev_dbg(dev,
- " #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n",
- i, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
- fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg);
+ "Pin index #%d: %uHz, %ubit (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, fmt->ch_map,
+ fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
+ pin_fmt[i].buffer_size);
+ }
+}
+
+static const struct sof_ipc4_audio_format *
+sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index)
+{
+ struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
+ struct sof_ipc4_process *process;
+ int i;
+
+ if (swidget->id != snd_soc_dapm_effect) {
+ struct sof_ipc4_base_module_cfg *base = swidget->private;
+
+ /* For non-process modules, base module config format is used for all input pins */
+ return &base->audio_fmt;
+ }
+
+ process = swidget->private;
+ base_cfg_ext = process->base_config_ext;
+
+ /*
+ * If there are multiple input formats available for a pin, the first available format
+ * is chosen.
+ */
+ for (i = 0; i < base_cfg_ext->num_input_pin_fmts; i++) {
+ struct sof_ipc4_pin_format *pin_format = &base_cfg_ext->pin_formats[i];
+
+ if (pin_format->pin_index == pin_index)
+ return &pin_format->audio_fmt;
}
+
+ return NULL;
}
/**
@@ -166,108 +205,99 @@ static void sof_ipc4_dbg_audio_format(struct device *dev,
* @scomp: pointer to pointer to SOC component
* @swidget: pointer to struct snd_sof_widget containing tuples
* @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in
- * @has_out_format: true if available_fmt contains output format
+ * @module_base_cfg: Pointer to the base_config in the module init IPC payload
*
* Return: 0 if successful
*/
static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
struct snd_sof_widget *swidget,
struct sof_ipc4_available_audio_format *available_fmt,
- bool has_out_format)
+ struct sof_ipc4_base_module_cfg *module_base_cfg)
{
- struct sof_ipc4_base_module_cfg *base_config;
- struct sof_ipc4_audio_format *out_format;
- int audio_fmt_num = 0;
- int ret, i;
+ struct sof_ipc4_pin_format *in_format = NULL;
+ struct sof_ipc4_pin_format *out_format;
+ int ret;
- ret = sof_update_ipc_object(scomp, &audio_fmt_num,
+ ret = sof_update_ipc_object(scomp, available_fmt,
SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(audio_fmt_num), 1);
- if (ret || audio_fmt_num <= 0) {
- dev_err(scomp->dev, "Invalid number of audio formats: %d\n", audio_fmt_num);
- return -EINVAL;
- }
- available_fmt->audio_fmt_num = audio_fmt_num;
-
- dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num);
-
- base_config = kcalloc(available_fmt->audio_fmt_num, sizeof(*base_config), GFP_KERNEL);
- if (!base_config)
- return -ENOMEM;
-
- /* set cpc and is_pages for all base_cfg */
- for (i = 0; i < available_fmt->audio_fmt_num; i++) {
- ret = sof_update_ipc_object(scomp, &base_config[i],
- SOF_COMP_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(*base_config), 1);
- if (ret) {
- dev_err(scomp->dev, "parse comp tokens failed %d\n", ret);
- goto err_in;
- }
+ swidget->num_tuples, sizeof(available_fmt), 1);
+ if (ret) {
+ dev_err(scomp->dev, "Failed to parse audio format token count\n");
+ return ret;
}
- /* copy the ibs/obs for each base_cfg */
- ret = sof_update_ipc_object(scomp, base_config,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(*base_config),
- available_fmt->audio_fmt_num);
- if (ret) {
- dev_err(scomp->dev, "parse buffer size tokens failed %d\n", ret);
- goto err_in;
+ if (!available_fmt->num_input_formats && !available_fmt->num_output_formats) {
+ dev_err(scomp->dev, "No input/output pin formats set in topology\n");
+ return -EINVAL;
}
- for (i = 0; i < available_fmt->audio_fmt_num; i++)
- dev_dbg(scomp->dev, "%d: ibs: %d obs: %d cpc: %d is_pages: %d\n", i,
- base_config[i].ibs, base_config[i].obs,
- base_config[i].cpc, base_config[i].is_pages);
+ dev_dbg(scomp->dev,
+ "Number of input audio formats: %d. Number of output audio formats: %d\n",
+ available_fmt->num_input_formats, available_fmt->num_output_formats);
- ret = sof_update_ipc_object(scomp, &base_config->audio_fmt,
- SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(*base_config),
- available_fmt->audio_fmt_num);
+ /* set cpc and is_pages in the module's base_config */
+ ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*module_base_cfg), 1);
if (ret) {
- dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret);
- goto err_in;
+ dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n",
+ swidget->widget->name, ret);
+ return ret;
}
- dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name);
- sof_ipc4_dbg_audio_format(scomp->dev, &base_config->audio_fmt,
- sizeof(*base_config),
- available_fmt->audio_fmt_num);
+ dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n",
+ swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages);
- available_fmt->base_config = base_config;
+ if (available_fmt->num_input_formats) {
+ in_format = kcalloc(available_fmt->num_input_formats,
+ sizeof(*in_format), GFP_KERNEL);
+ if (!in_format)
+ return -ENOMEM;
+ available_fmt->input_pin_fmts = in_format;
- if (!has_out_format)
- return 0;
+ ret = sof_update_ipc_object(scomp, in_format,
+ SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*in_format),
+ available_fmt->num_input_formats);
+ if (ret) {
+ dev_err(scomp->dev, "parse input audio fmt tokens failed %d\n", ret);
+ goto err_in;
+ }
- out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL);
- if (!out_format) {
- ret = -ENOMEM;
- goto err_in;
+ dev_dbg(scomp->dev, "Input audio formats for %s\n", swidget->widget->name);
+ sof_ipc4_dbg_audio_format(scomp->dev, in_format,
+ available_fmt->num_input_formats);
}
- ret = sof_update_ipc_object(scomp, out_format,
- SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(*out_format),
- available_fmt->audio_fmt_num);
+ if (available_fmt->num_output_formats) {
+ out_format = kcalloc(available_fmt->num_output_formats, sizeof(*out_format),
+ GFP_KERNEL);
+ if (!out_format) {
+ ret = -ENOMEM;
+ goto err_in;
+ }
- if (ret) {
- dev_err(scomp->dev, "parse output audio_fmt tokens failed\n");
- goto err_out;
- }
+ ret = sof_update_ipc_object(scomp, out_format,
+ SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*out_format),
+ available_fmt->num_output_formats);
+ if (ret) {
+ dev_err(scomp->dev, "parse output audio fmt tokens failed\n");
+ goto err_out;
+ }
- available_fmt->out_audio_fmt = out_format;
- dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name);
- sof_ipc4_dbg_audio_format(scomp->dev, out_format, sizeof(*out_format),
- available_fmt->audio_fmt_num);
+ available_fmt->output_pin_fmts = out_format;
+ dev_dbg(scomp->dev, "Output audio formats for %s\n", swidget->widget->name);
+ sof_ipc4_dbg_audio_format(scomp->dev, out_format,
+ available_fmt->num_output_formats);
+ }
return 0;
err_out:
kfree(out_format);
err_in:
- kfree(base_config);
-
+ kfree(in_format);
+ available_fmt->input_pin_fmts = NULL;
return ret;
}
@@ -275,10 +305,10 @@ err_in:
static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)
{
- kfree(available_fmt->base_config);
- available_fmt->base_config = NULL;
- kfree(available_fmt->out_audio_fmt);
- available_fmt->out_audio_fmt = NULL;
+ kfree(available_fmt->output_pin_fmts);
+ available_fmt->output_pin_fmts = NULL;
+ kfree(available_fmt->input_pin_fmts);
+ available_fmt->input_pin_fmts = NULL;
}
static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget)
@@ -326,13 +356,31 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_
return 0;
}
+static void sof_ipc4_widget_update_kcontrol_module_id(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_fw_module *fw_module = swidget->module_info;
+ struct snd_sof_control *scontrol;
+
+ /* update module ID for all kcontrols for this widget */
+ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
+ if (scontrol->comp_id == swidget->comp_id) {
+ struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+ struct sof_ipc4_msg *msg = &cdata->msg;
+
+ msg->primary |= fw_module->man4_module_entry.id;
+ }
+ }
+}
+
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;
int node_type = 0;
- int ret, i;
+ int ret;
ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
if (!ipc4_copier)
@@ -343,17 +391,11 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
- ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
+ &ipc4_copier->data.base_config);
if (ret)
goto free_copier;
- available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
- GFP_KERNEL);
- if (!available_fmt->dma_buffer_size) {
- ret = -ENOMEM;
- goto free_available_fmt;
- }
-
/*
* This callback is used by host copier and module-to-module copier,
* and only host copier needs to set gtw_cfg.
@@ -361,21 +403,6 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
if (!WIDGET_IS_AIF(swidget->id))
goto skip_gtw_cfg;
- ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
- SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(u32),
- available_fmt->audio_fmt_num);
- if (ret) {
- dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
- swidget->widget->name);
- goto err;
- }
-
- dev_dbg(scomp->dev, "dma buffer size:\n");
- for (i = 0; i < available_fmt->audio_fmt_num; i++)
- dev_dbg(scomp->dev, "%d: %u\n", i,
- available_fmt->dma_buffer_size[i]);
-
ret = sof_update_ipc_object(scomp, &node_type,
SOF_COPIER_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(node_type), 1);
@@ -383,7 +410,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
if (ret) {
dev_err(scomp->dev, "parse host copier node type token failed %d\n",
ret);
- goto err;
+ goto free_available_fmt;
}
dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
@@ -391,7 +418,7 @@ skip_gtw_cfg:
ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
if (!ipc4_copier->gtw_attr) {
ret = -ENOMEM;
- goto err;
+ goto free_available_fmt;
}
ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
@@ -422,8 +449,6 @@ skip_gtw_cfg:
free_gtw_attr:
kfree(ipc4_copier->gtw_attr);
-err:
- kfree(available_fmt->dma_buffer_size);
free_available_fmt:
sof_ipc4_free_audio_fmt(available_fmt);
free_copier:
@@ -441,9 +466,7 @@ static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget)
return;
available_fmt = &ipc4_copier->available_fmt;
- kfree(available_fmt->dma_buffer_size);
- kfree(available_fmt->base_config);
- kfree(available_fmt->out_audio_fmt);
+ kfree(available_fmt->output_pin_fmts);
kfree(ipc4_copier->gtw_attr);
kfree(ipc4_copier);
swidget->private = NULL;
@@ -455,8 +478,10 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier;
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
int node_type = 0;
- int ret, i;
+ int ret;
ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
if (!ipc4_copier)
@@ -466,37 +491,17 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
- ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
+ &ipc4_copier->data.base_config);
if (ret)
goto free_copier;
- available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
- GFP_KERNEL);
- if (!available_fmt->dma_buffer_size) {
- ret = -ENOMEM;
- goto free_available_fmt;
- }
-
- ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
- SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
- swidget->num_tuples, sizeof(u32),
- available_fmt->audio_fmt_num);
- if (ret) {
- dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
- swidget->widget->name);
- goto err;
- }
-
- for (i = 0; i < available_fmt->audio_fmt_num; i++)
- dev_dbg(scomp->dev, "%d: dma buffer size: %u\n", i,
- available_fmt->dma_buffer_size[i]);
-
ret = sof_update_ipc_object(scomp, &node_type,
SOF_COPIER_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(node_type), 1);
if (ret) {
dev_err(scomp->dev, "parse dai node type failed %d\n", ret);
- goto err;
+ goto free_available_fmt;
}
ret = sof_update_ipc_object(scomp, ipc4_copier,
@@ -504,7 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
swidget->num_tuples, sizeof(u32), 1);
if (ret) {
dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret);
- goto err;
+ goto free_available_fmt;
}
dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
@@ -512,17 +517,43 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+ if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
+ dev_err(scomp->dev,
+ "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
+ ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);
+ ret = -ENODEV;
+ goto free_available_fmt;
+ }
+
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_ALH:
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_alh_configuration_blob *blob;
+ struct snd_soc_dapm_path *p;
struct snd_sof_widget *w;
+ int src_num = 0;
+
+ snd_soc_dapm_widget_for_each_source_path(swidget->widget, p)
+ src_num++;
+
+ if (swidget->id == snd_soc_dapm_dai_in && src_num == 0) {
+ /*
+ * The blob will not be used if the ALH copier is playback direction
+ * and doesn't connect to any source.
+ * It is fine to call kfree(ipc4_copier->copier_config) since
+ * ipc4_copier->copier_config is null.
+ */
+ ret = 0;
+ break;
+ }
blob = kzalloc(sizeof(*blob), GFP_KERNEL);
if (!blob) {
ret = -ENOMEM;
- goto err;
+ goto free_available_fmt;
}
list_for_each_entry(w, &sdev->widget_list, list) {
@@ -551,7 +582,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
if (!ipc4_copier->gtw_attr) {
ret = -ENOMEM;
- goto err;
+ goto free_available_fmt;
}
ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
@@ -572,8 +603,6 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
free_copier_config:
kfree(ipc4_copier->copier_config);
-err:
- kfree(available_fmt->dma_buffer_size);
free_available_fmt:
sof_ipc4_free_audio_fmt(available_fmt);
free_copier:
@@ -601,9 +630,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
ipc4_copier = dai->private;
available_fmt = &ipc4_copier->available_fmt;
- kfree(available_fmt->dma_buffer_size);
- kfree(available_fmt->base_config);
- kfree(available_fmt->out_audio_fmt);
+ kfree(available_fmt->output_pin_fmts);
if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP &&
ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC)
kfree(ipc4_copier->copier_config);
@@ -629,6 +656,14 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
goto err;
}
+ swidget->core = pipeline->core_id;
+
+ if (pipeline->use_chain_dma) {
+ dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
+ swidget->private = pipeline;
+ return 0;
+ }
+
/* parse one set of pipeline tokens */
ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(*swidget), 1);
@@ -640,9 +675,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
/* TODO: Get priority from topology */
pipeline->priority = 0;
- dev_dbg(scomp->dev, "pipeline '%s': id %d pri %d lp mode %d\n",
+ dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
swidget->widget->name, swidget->pipeline_id,
- pipeline->priority, pipeline->lp_mode);
+ pipeline->priority, pipeline->core_id, pipeline->lp_mode);
swidget->private = pipeline;
@@ -652,6 +687,7 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
pipeline->msg.extension = pipeline->lp_mode;
+ pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_CORE_ID(pipeline->core_id);
pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
return 0;
@@ -663,9 +699,6 @@ err:
static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
{
struct snd_soc_component *scomp = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc4_fw_module *fw_module;
- struct snd_sof_control *scontrol;
struct sof_ipc4_gain *gain;
int ret;
@@ -678,8 +711,7 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
gain->data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
gain->data.init_val = SOF_IPC4_VOL_ZERO_DB;
- /* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
- ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, false);
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, &gain->base_config);
if (ret)
goto err;
@@ -699,16 +731,7 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
if (ret)
goto err;
- fw_module = swidget->module_info;
-
- /* update module ID for all kcontrols for this widget */
- list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
- if (scontrol->comp_id == swidget->comp_id) {
- struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
- struct sof_ipc4_msg *msg = &cdata->msg;
-
- msg->primary |= fw_module->man4_module_entry.id;
- }
+ sof_ipc4_widget_update_kcontrol_module_id(swidget);
return 0;
err:
@@ -744,8 +767,8 @@ static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
swidget->private = mixer;
- /* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
- ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt, false);
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt,
+ &mixer->base_config);
if (ret)
goto err;
@@ -775,8 +798,7 @@ static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)
swidget->private = src;
- /* The out_audio_fmt in topology is ignored as it is not required by SRC */
- ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, false);
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, &src->base_config);
if (ret)
goto err;
@@ -825,6 +847,94 @@ static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
swidget->private = NULL;
}
+/*
+ * Add the process modules support. The process modules are defined as snd_soc_dapm_effect modules.
+ */
+static int sof_ipc4_widget_setup_comp_process(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct sof_ipc4_fw_module *fw_module;
+ struct sof_ipc4_process *process;
+ void *cfg;
+ int ret;
+
+ process = kzalloc(sizeof(*process), GFP_KERNEL);
+ if (!process)
+ return -ENOMEM;
+
+ swidget->private = process;
+
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, &process->available_fmt,
+ &process->base_config);
+ if (ret)
+ goto err;
+
+ ret = sof_ipc4_widget_setup_msg(swidget, &process->msg);
+ if (ret)
+ goto err;
+
+ /* parse process init module payload config type from module info */
+ fw_module = swidget->module_info;
+ process->init_config = FIELD_GET(SOF_IPC4_MODULE_INIT_CONFIG_MASK,
+ fw_module->man4_module_entry.type);
+
+ process->ipc_config_size = sizeof(struct sof_ipc4_base_module_cfg);
+
+ /* allocate memory for base config extension if needed */
+ if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
+ struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
+ u32 ext_size = struct_size(base_cfg_ext, pin_formats,
+ swidget->num_input_pins + swidget->num_output_pins);
+
+ base_cfg_ext = kzalloc(ext_size, GFP_KERNEL);
+ if (!base_cfg_ext) {
+ ret = -ENOMEM;
+ goto free_available_fmt;
+ }
+
+ base_cfg_ext->num_input_pin_fmts = swidget->num_input_pins;
+ base_cfg_ext->num_output_pin_fmts = swidget->num_output_pins;
+ process->base_config_ext = base_cfg_ext;
+ process->base_config_ext_size = ext_size;
+ process->ipc_config_size += ext_size;
+ }
+
+ cfg = kzalloc(process->ipc_config_size, GFP_KERNEL);
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto free_base_cfg_ext;
+ }
+
+ process->ipc_config_data = cfg;
+
+ sof_ipc4_widget_update_kcontrol_module_id(swidget);
+
+ return 0;
+free_base_cfg_ext:
+ kfree(process->base_config_ext);
+ process->base_config_ext = NULL;
+free_available_fmt:
+ sof_ipc4_free_audio_fmt(&process->available_fmt);
+err:
+ kfree(process);
+ swidget->private = NULL;
+ return ret;
+}
+
+static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget)
+{
+ struct sof_ipc4_process *process = swidget->private;
+
+ if (!process)
+ return;
+
+ kfree(process->ipc_config_data);
+ kfree(process->base_config_ext);
+ sof_ipc4_free_audio_fmt(&process->available_fmt);
+ kfree(swidget->private);
+ swidget->private = NULL;
+}
+
static void
sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config)
@@ -876,22 +986,62 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
return 0;
}
+/* 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)
+{
+ snd_pcm_format_t snd_fmt;
+ struct snd_interval *i;
+ struct snd_mask *m;
+ int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
+ unsigned int channels, rate;
+
+ switch (valid_bits) {
+ case 16:
+ snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 24:
+ snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 32:
+ snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
+ return -EINVAL;
+ }
+
+ m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(m);
+ snd_mask_set_format(m, snd_fmt);
+
+ rate = fmt->sampling_frequency;
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ i->min = rate;
+ i->max = rate;
+
+ channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ i->min = channels;
+ i->max = channels;
+
+ return 0;
+}
+
static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config,
- struct sof_ipc4_audio_format *out_format,
struct snd_pcm_hw_params *params,
struct sof_ipc4_available_audio_format *available_fmt,
- size_t object_offset)
+ struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
{
- void *ptr = available_fmt->ref_audio_fmt;
u32 valid_bits;
u32 channels;
u32 rate;
int sample_valid_bits;
int i;
- if (!ptr) {
+ if (!pin_fmts) {
dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name);
return -EINVAL;
}
@@ -911,53 +1061,53 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
return -EINVAL;
}
- if (!available_fmt->audio_fmt_num) {
+ if (!pin_fmts_size) {
dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name);
return -EINVAL;
}
/*
- * Search supported audio formats to match rate, channels ,and
+ * Search supported audio formats with pin index 0 to match rate, channels ,and
* sample_valid_bytes from runtime params
*/
- for (i = 0; i < available_fmt->audio_fmt_num; i++, ptr = (u8 *)ptr + object_offset) {
- struct sof_ipc4_audio_format *fmt = ptr;
+ for (i = 0; i < pin_fmts_size; i++) {
+ struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
+
+ if (pin_fmts[i].pin_index)
+ continue;
rate = fmt->sampling_frequency;
channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
if (params_rate(params) == rate && params_channels(params) == channels &&
sample_valid_bits == valid_bits) {
- dev_dbg(sdev->dev, "matching audio format index for %uHz, %ubit, %u channels: %d\n",
+ dev_dbg(sdev->dev, "matched audio format index for %uHz, %ubit, %u channels: %d\n",
rate, valid_bits, channels, i);
-
- /* copy ibs/obs and input format */
- memcpy(base_config, &available_fmt->base_config[i],
- sizeof(struct sof_ipc4_base_module_cfg));
-
- /* copy output format */
- if (out_format)
- memcpy(out_format, &available_fmt->out_audio_fmt[i],
- sizeof(struct sof_ipc4_audio_format));
break;
}
}
- if (i == available_fmt->audio_fmt_num) {
+ if (i == pin_fmts_size) {
dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
__func__, params_rate(params), sample_valid_bits, params_channels(params));
return -EINVAL;
}
- dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
- sof_ipc4_dbg_audio_format(sdev->dev, &base_config->audio_fmt,
- sizeof(*base_config), 1);
- if (out_format) {
- dev_dbg(sdev->dev, "Init output audio formats for %s\n", swidget->widget->name);
- sof_ipc4_dbg_audio_format(sdev->dev, out_format,
- sizeof(*out_format), 1);
+ /* copy input format */
+ if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {
+ memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,
+ sizeof(struct sof_ipc4_audio_format));
+
+ /* set base_cfg ibs/obs */
+ base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size;
+
+ dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
+ sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);
}
+ if (available_fmt->num_output_formats && i < available_fmt->num_output_formats)
+ base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
+
/* Return the index of the matched format */
return i;
}
@@ -974,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
pipeline->mem_usage = 0;
if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary = 0;
+ pipeline->msg.extension = 0;
+ }
ipc4_copier = swidget->private;
} else if (WIDGET_IS_DAI(swidget->id)) {
struct snd_sof_dai *dai = swidget->private;
ipc4_copier = dai->private;
+
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary = 0;
+ pipeline->msg.extension = 0;
+ }
+
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
struct sof_ipc4_alh_configuration_blob *blob;
@@ -1109,6 +1269,69 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
}
#endif
+static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth)
+{
+ switch (bit_depth) {
+ case 16:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ break;
+ case 24:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ break;
+ case 32:
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev,
+ struct snd_pcm_hw_params *pipeline_params,
+ struct snd_pcm_hw_params *fe_params,
+ struct sof_ipc4_available_audio_format *available_fmt)
+{
+ struct sof_ipc4_audio_format *audio_fmt;
+ unsigned int sample_valid_bits;
+ bool multiple_formats = false;
+ bool fe_format_match = false;
+ struct snd_mask *fmt;
+ int i;
+
+ for (i = 0; i < available_fmt->num_output_formats; i++) {
+ unsigned int val;
+
+ audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
+ val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg);
+
+ if (i == 0)
+ sample_valid_bits = val;
+ else if (sample_valid_bits != val)
+ multiple_formats = true;
+
+ if (snd_pcm_format_width(params_format(fe_params)) == val)
+ fe_format_match = true;
+ }
+
+ fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(fmt);
+
+ if (multiple_formats) {
+ if (fe_format_match) {
+ /* multiple formats defined and one matches FE */
+ snd_mask_set_format(fmt, params_format(fe_params));
+ return 0;
+ }
+
+ dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n");
+ return -EINVAL;
+ }
+
+ return ipc4_set_fmt_mask(fmt, sample_valid_bits);
+}
+
static int
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_pcm_hw_params *fe_params,
@@ -1118,17 +1341,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc4_pin_format *format_list_to_search;
struct sof_ipc4_copier_data *copier_data;
struct snd_pcm_hw_params *ref_params;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_dai *dai;
struct snd_mask *fmt;
int out_sample_valid_bits;
- size_t ref_audio_fmt_size;
void **ipc_config_data;
int *ipc_config_size;
u32 **data;
int ipc_size, ret;
+ u32 deep_buffer_dma_ms = 0;
+ u32 format_list_count;
dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
@@ -1140,25 +1365,66 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
- pipe_widget = swidget->spipe->pipe_widget;
- pipeline = pipe_widget->private;
+ /* parse the deep buffer dma size */
+ ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
+ SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(u32), 1);
+ if (ret) {
+ dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n",
+ swidget->widget->name);
+ return ret;
+ }
+
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
gtw_attr = ipc4_copier->gtw_attr;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ if (pipeline->use_chain_dma) {
+ u32 host_dma_id;
+ u32 fifo_size;
+
+ host_dma_id = platform_params->stream_tag - 1;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
+
+ /* Set SCS bit for S16_LE format only */
+ if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
+
+ /*
+ * Despite its name the bitfield 'fifo_size' is used to define DMA buffer
+ * size. The expression calculates 2ms buffer size.
+ */
+ fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS *
+ params_rate(fe_params) *
+ params_channels(fe_params) *
+ params_physical_width(fe_params)), 8000);
+ pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);
+
+ /*
+ * Chain DMA does not support stream timestamping, set node_id to invalid
+ * to skip the code in sof_ipc4_get_stream_start_offset().
+ */
+ copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
+
+ return 0;
+ }
+
/*
- * base_config->audio_fmt and out_audio_fmt represent the input and output audio
- * formats. Use the input format as the reference to match pcm params for playback
- * and the output format as reference for capture.
+ * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
+ * for capture.
*/
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
- available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
- ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+ format_list_to_search = available_fmt->input_pin_fmts;
+ format_list_count = available_fmt->num_input_formats;
} else {
- available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
- ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
+ format_list_to_search = available_fmt->output_pin_fmts;
+ format_list_count = available_fmt->num_output_formats;
}
+
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |=
SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);
@@ -1171,25 +1437,28 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
{
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->use_chain_dma)
+ return 0;
+
dai = swidget->private;
ipc4_copier = (struct sof_ipc4_copier *)dai->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
if (dir == SNDRV_PCM_STREAM_CAPTURE) {
- available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
- ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
+ format_list_to_search = available_fmt->output_pin_fmts;
+ format_list_count = available_fmt->num_output_formats;
- /*
- * modify the input params for the dai copier as it only supports
- * 32-bit always
- */
- fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
- snd_mask_none(fmt);
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+ ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params,
+ available_fmt);
+ if (ret < 0)
+ return ret;
} else {
- available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
- ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+ format_list_to_search = available_fmt->input_pin_fmts;
+ format_list_count = available_fmt->num_input_formats;
}
ref_params = pipeline_params;
@@ -1209,12 +1478,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
- /*
- * base_config->audio_fmt represent the input audio formats. Use
- * the input format as the reference to match pcm params
- */
- available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
- ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+ /* Use the input formats to match pcm params */
+ format_list_to_search = available_fmt->input_pin_fmts;
+ format_list_count = available_fmt->num_input_formats;
ref_params = pipeline_params;
break;
@@ -1226,12 +1492,23 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
/* set input and output audio formats */
- ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config,
- &copier_data->out_format, ref_params,
- available_fmt, ref_audio_fmt_size);
+ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
+ available_fmt, format_list_to_search, format_list_count);
if (ret < 0)
return ret;
+ /*
+ * Set the output format. Current topology defines pin 0 input and output formats in pairs.
+ * This assumes that the pin 0 formats are defined before all other pins.
+ * So pick the output audio format with the same index as the chosen
+ * input format. This logic will need to be updated when the format definitions
+ * in topology change.
+ */
+ memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+ sizeof(struct sof_ipc4_audio_format));
+ dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);
+ sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1);
+
switch (swidget->id) {
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
@@ -1323,25 +1600,34 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
out_sample_valid_bits =
SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg);
snd_mask_none(fmt);
- switch (out_sample_valid_bits) {
- case 16:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
+ ret = ipc4_set_fmt_mask(fmt, out_sample_valid_bits);
+ if (ret)
+ return ret;
+
+ /*
+ * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the
+ * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set
+ * in topology.
+ */
+ switch (swidget->id) {
+ case snd_soc_dapm_dai_in:
+ copier_data->gtw_cfg.dma_buffer_size =
+ SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs;
break;
- case 24:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ case snd_soc_dapm_aif_in:
+ copier_data->gtw_cfg.dma_buffer_size =
+ max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
+ copier_data->base_config.ibs;
break;
- case 32:
- snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
+ 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;
default:
- dev_err(sdev->dev, "invalid sample frame format %d\n",
- params_format(pipeline_params));
- return -EINVAL;
+ break;
}
- /* set the gateway dma_buffer_size using the matched ID returned above */
- copier_data->gtw_cfg.dma_buffer_size = available_fmt->dma_buffer_size[ret];
-
data = &ipc4_copier->copier_config;
ipc_config_size = &ipc4_copier->ipc_config_size;
ipc_config_data = &ipc4_copier->ipc_config_data;
@@ -1377,14 +1663,13 @@ static int sof_ipc4_prepare_gain_module(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_gain *gain = swidget->private;
+ struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
int ret;
- gain->available_fmt.ref_audio_fmt = &gain->available_fmt.base_config->audio_fmt;
-
- /* output format is not required to be sent to the FW for gain */
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
- NULL, pipeline_params, &gain->available_fmt,
- sizeof(gain->base_config));
+ pipeline_params, available_fmt,
+ available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
if (ret < 0)
return ret;
@@ -1402,15 +1687,13 @@ static int sof_ipc4_prepare_mixer_module(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_mixer *mixer = swidget->private;
+ struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
int ret;
- /* only 32bit is supported by mixer */
- mixer->available_fmt.ref_audio_fmt = &mixer->available_fmt.base_config->audio_fmt;
-
- /* output format is not required to be sent to the FW for mixer */
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
- NULL, pipeline_params, &mixer->available_fmt,
- sizeof(mixer->base_config));
+ pipeline_params, available_fmt,
+ available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
if (ret < 0)
return ret;
@@ -1428,15 +1711,14 @@ static int sof_ipc4_prepare_src_module(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_src *src = swidget->private;
+ struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
struct snd_interval *rate;
int ret;
- src->available_fmt.ref_audio_fmt = &src->available_fmt.base_config->audio_fmt;
-
- /* output format is not required to be sent to the FW for SRC */
ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
- NULL, pipeline_params, &src->available_fmt,
- sizeof(src->base_config));
+ pipeline_params, available_fmt,
+ available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
if (ret < 0)
return ret;
@@ -1451,6 +1733,135 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
return 0;
}
+static int
+sof_ipc4_process_set_pin_formats(struct snd_sof_widget *swidget, int pin_type)
+{
+ struct sof_ipc4_process *process = swidget->private;
+ struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
+ struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+ struct sof_ipc4_pin_format *pin_format, *format_list_to_search;
+ struct snd_soc_component *scomp = swidget->scomp;
+ int num_pins, format_list_count;
+ int pin_format_offset = 0;
+ int i, j;
+
+ /* set number of pins, offset of pin format and format list to search based on pin type */
+ if (pin_type == SOF_PIN_TYPE_INPUT) {
+ num_pins = swidget->num_input_pins;
+ format_list_to_search = available_fmt->input_pin_fmts;
+ format_list_count = available_fmt->num_input_formats;
+ } else {
+ num_pins = swidget->num_output_pins;
+ pin_format_offset = swidget->num_input_pins;
+ format_list_to_search = available_fmt->output_pin_fmts;
+ format_list_count = available_fmt->num_output_formats;
+ }
+
+ for (i = pin_format_offset; i < num_pins + pin_format_offset; i++) {
+ pin_format = &base_cfg_ext->pin_formats[i];
+
+ /* Pin 0 audio formats are derived from the base config input/output format */
+ if (i == pin_format_offset) {
+ if (pin_type == SOF_PIN_TYPE_INPUT) {
+ pin_format->buffer_size = process->base_config.ibs;
+ pin_format->audio_fmt = process->base_config.audio_fmt;
+ } else {
+ pin_format->buffer_size = process->base_config.obs;
+ pin_format->audio_fmt = process->output_format;
+ }
+ continue;
+ }
+
+ /*
+ * For all other pins, find the pin formats from those set in topology. If there
+ * is more than one format specified for a pin, this will pick the first available
+ * one.
+ */
+ for (j = 0; j < format_list_count; j++) {
+ struct sof_ipc4_pin_format *pin_format_item = &format_list_to_search[j];
+
+ if (pin_format_item->pin_index == i - pin_format_offset) {
+ *pin_format = *pin_format_item;
+ break;
+ }
+ }
+
+ if (j == format_list_count) {
+ dev_err(scomp->dev, "%s pin %d format not found for %s\n",
+ (pin_type == SOF_PIN_TYPE_INPUT) ? "input" : "output",
+ i - pin_format_offset, swidget->widget->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int sof_ipc4_process_add_base_cfg_extn(struct snd_sof_widget *swidget)
+{
+ int ret, i;
+
+ /* copy input and output pin formats */
+ for (i = 0; i <= SOF_PIN_TYPE_OUTPUT; i++) {
+ ret = sof_ipc4_process_set_pin_formats(swidget, i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
+ struct snd_pcm_hw_params *fe_params,
+ struct snd_sof_platform_stream_params *platform_params,
+ struct snd_pcm_hw_params *pipeline_params, int dir)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct sof_ipc4_process *process = swidget->private;
+ struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
+ void *cfg = process->ipc_config_data;
+ int ret;
+
+ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config,
+ pipeline_params, available_fmt,
+ available_fmt->input_pin_fmts,
+ available_fmt->num_input_formats);
+ if (ret < 0)
+ return ret;
+
+ /* copy Pin 0 output format */
+ if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats &&
+ !available_fmt->output_pin_fmts[ret].pin_index) {
+ memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt,
+ sizeof(struct sof_ipc4_audio_format));
+
+ /* modify the pipeline params with the pin 0 output format */
+ ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
+ if (ret)
+ return ret;
+ }
+
+ /* update pipeline memory usage */
+ sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &process->base_config);
+
+ /* ipc_config_data is composed of the base_config followed by an optional extension */
+ memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg));
+ cfg += sizeof(struct sof_ipc4_base_module_cfg);
+
+ if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
+ struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
+
+ ret = sof_ipc4_process_add_base_cfg_extn(swidget);
+ if (ret < 0)
+ return ret;
+
+ memcpy(cfg, base_cfg_ext, process->base_config_ext_size);
+ }
+
+ return 0;
+}
+
static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
struct sof_ipc4_control_data *control_data;
@@ -1483,6 +1894,71 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof
return 0;
}
+static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
+{
+ struct sof_ipc4_control_data *control_data;
+ struct sof_ipc4_msg *msg;
+ int ret;
+
+ if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {
+ dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",
+ scontrol->name, scontrol->max_size);
+ return -EINVAL;
+ }
+
+ if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {
+ dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",
+ scontrol->name, scontrol->priv_size,
+ scontrol->max_size - sizeof(*control_data));
+ return -EINVAL;
+ }
+
+ scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;
+
+ scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
+ if (!scontrol->ipc_control_data)
+ return -ENOMEM;
+
+ control_data = scontrol->ipc_control_data;
+ control_data->index = scontrol->index;
+ if (scontrol->priv_size > 0) {
+ memcpy(control_data->data, scontrol->priv, scontrol->priv_size);
+ kfree(scontrol->priv);
+ scontrol->priv = NULL;
+
+ if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) {
+ dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n",
+ control_data->data->magic, scontrol->name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* TODO: check the ABI version */
+
+ if (control_data->data->size + sizeof(struct sof_abi_hdr) !=
+ scontrol->priv_size) {
+ dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n",
+ scontrol->name,
+ control_data->data->size + sizeof(struct sof_abi_hdr),
+ scontrol->priv_size);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ msg = &control_data->msg;
+ msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+ msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+ return 0;
+
+err:
+ kfree(scontrol->ipc_control_data);
+ scontrol->ipc_control_data = NULL;
+ return ret;
+}
+
static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
switch (scontrol->info_type) {
@@ -1490,6 +1966,8 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
case SND_SOC_TPLG_CTL_VOLSW_SX:
case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
return sof_ipc4_control_load_volume(sdev, scontrol);
+ case SND_SOC_TPLG_CTL_BYTES:
+ return sof_ipc4_control_load_bytes(sdev, scontrol);
default:
break;
}
@@ -1511,6 +1989,12 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
case snd_soc_dapm_scheduler:
pipeline = swidget->private;
+ if (pipeline->use_chain_dma) {
+ dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
+ swidget->widget->name);
+ return 0;
+ }
+
dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,
pipeline->mem_usage);
@@ -1533,6 +2017,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
{
struct sof_ipc4_copier *ipc4_copier = swidget->private;
+ pipeline = pipe_widget->private;
+ if (pipeline->use_chain_dma)
+ return 0;
+
ipc_size = ipc4_copier->ipc_config_size;
ipc_data = ipc4_copier->ipc_config_data;
@@ -1545,6 +2033,10 @@ 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;
+ pipeline = pipe_widget->private;
+ if (pipeline->use_chain_dma)
+ return 0;
+
ipc_size = ipc4_copier->ipc_config_size;
ipc_data = ipc4_copier->ipc_config_data;
@@ -1582,6 +2074,22 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg = &src->msg;
break;
}
+ case snd_soc_dapm_effect:
+ {
+ struct sof_ipc4_process *process = swidget->private;
+
+ if (!process->ipc_config_size) {
+ dev_err(sdev->dev, "module %s has no config data!\n",
+ swidget->widget->name);
+ return -EINVAL;
+ }
+
+ ipc_size = process->ipc_config_size;
+ ipc_data = process->ipc_config_data;
+
+ msg = &process->msg;
+ break;
+ }
default:
dev_err(sdev->dev, "widget type %d not supported", swidget->id);
return -EINVAL;
@@ -1610,7 +2118,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg->data_size = ipc_size;
msg->data_ptr = ipc_data;
- ret = sof_ipc_tx_message(sdev->ipc, msg, ipc_size, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);
if (ret < 0) {
dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
@@ -1640,6 +2148,13 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
struct sof_ipc4_msg msg = {{ 0 }};
u32 header;
+ if (pipeline->use_chain_dma) {
+ dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
+ swidget->widget->name);
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ return 0;
+ }
+
header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);
header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
@@ -1647,7 +2162,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
msg.primary = header;
- ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
if (ret < 0)
dev_err(sdev->dev, "failed to free pipeline widget %s\n",
swidget->widget->name);
@@ -1656,7 +2171,11 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
ida_free(&pipeline_ida, swidget->instance_id);
} else {
- ida_free(&fw_module->m_ida, swidget->instance_id);
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (!pipeline->use_chain_dma)
+ ida_free(&fw_module->m_ida, swidget->instance_id);
}
mutex_unlock(&ipc4_data->pipeline_state_mutex);
@@ -1675,17 +2194,17 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
u32 num_pins;
int i;
- if (pin_type == SOF_PIN_TYPE_SOURCE) {
+ if (pin_type == SOF_PIN_TYPE_OUTPUT) {
current_swidget = src_widget;
- pin_binding = src_widget->src_pin_binding;
- queue_ida = &src_widget->src_queue_ida;
- num_pins = src_widget->num_source_pins;
+ pin_binding = src_widget->output_pin_binding;
+ queue_ida = &src_widget->output_queue_ida;
+ num_pins = src_widget->num_output_pins;
buddy_name = sink_widget->widget->name;
} else {
current_swidget = sink_widget;
- pin_binding = sink_widget->sink_pin_binding;
- queue_ida = &sink_widget->sink_queue_ida;
- num_pins = sink_widget->num_sink_pins;
+ pin_binding = sink_widget->input_pin_binding;
+ queue_ida = &sink_widget->input_queue_ida;
+ num_pins = sink_widget->num_input_pins;
buddy_name = src_widget->widget->name;
}
@@ -1693,12 +2212,12 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
if (num_pins < 1) {
dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",
- (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
+ (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
num_pins, current_swidget->widget->name);
return -EINVAL;
}
- /* If there is only one sink/source pin, queue id must be 0 */
+ /* If there is only one input/output pin, queue id must be 0 */
if (num_pins == 1)
return 0;
@@ -1713,7 +2232,7 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
* mixed use pin binding array and ida for queue ID allocation.
*/
dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",
- (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
+ (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
current_swidget->widget->name);
return -EINVAL;
}
@@ -1729,14 +2248,14 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
char **pin_binding;
int num_pins;
- if (pin_type == SOF_PIN_TYPE_SOURCE) {
- pin_binding = swidget->src_pin_binding;
- queue_ida = &swidget->src_queue_ida;
- num_pins = swidget->num_source_pins;
+ if (pin_type == SOF_PIN_TYPE_OUTPUT) {
+ pin_binding = swidget->output_pin_binding;
+ queue_ida = &swidget->output_queue_ida;
+ num_pins = swidget->num_output_pins;
} else {
- pin_binding = swidget->sink_pin_binding;
- queue_ida = &swidget->sink_queue_ida;
- num_pins = swidget->num_sink_pins;
+ pin_binding = swidget->input_pin_binding;
+ queue_ida = &swidget->input_queue_ida;
+ num_pins = swidget->num_input_pins;
}
/* Nothing to free if queue ID is not allocated with ida. */
@@ -1751,9 +2270,9 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
struct snd_sof_widget *sink_widget,
int sink_id)
{
- struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
- struct sof_ipc4_base_module_cfg *src_config;
struct sof_ipc4_copier_config_set_sink_format format;
+ struct sof_ipc4_base_module_cfg *src_config;
+ const struct sof_ipc4_audio_format *pin_fmt;
struct sof_ipc4_fw_module *fw_module;
struct sof_ipc4_msg msg = {{ 0 }};
u32 header, extension;
@@ -1773,7 +2292,16 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
format.sink_id = sink_id;
memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
- memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
+
+ pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id);
+ if (!pin_fmt) {
+ dev_err(sdev->dev, "Unable to get pin %d format for %s",
+ sink_id, sink_widget->widget->name);
+ return -EINVAL;
+ }
+
+ memcpy(&format.sink_fmt, pin_fmt, sizeof(format.sink_fmt));
+
msg.data_size = sizeof(format);
msg.data_ptr = &format;
@@ -1792,21 +2320,46 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
msg.primary = header;
msg.extension = extension;
- return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
+ return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, msg.data_size);
}
static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
{
struct snd_sof_widget *src_widget = sroute->src_widget;
struct snd_sof_widget *sink_widget = sroute->sink_widget;
+ struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
+ struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
+ struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
+ struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
struct sof_ipc4_msg msg = {{ 0 }};
u32 header, extension;
int ret;
+ /* no route set up if chain DMA is used */
+ if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) {
+ if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) {
+ dev_err(sdev->dev,
+ "use_chain_dma must be set for both src %s and sink %s pipelines\n",
+ src_widget->widget->name, sink_widget->widget->name);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ if (!src_fw_module || !sink_fw_module) {
+ dev_err(sdev->dev,
+ "cannot bind %s -> %s, no firmware module for: %s%s\n",
+ src_widget->widget->name, sink_widget->widget->name,
+ src_fw_module ? "" : " source",
+ sink_fw_module ? "" : " sink");
+
+ return -ENODEV;
+ }
+
sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
- SOF_PIN_TYPE_SOURCE);
+ SOF_PIN_TYPE_OUTPUT);
if (sroute->src_queue_id < 0) {
dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
src_widget->widget->name);
@@ -1814,12 +2367,12 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
}
sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
- SOF_PIN_TYPE_SINK);
+ SOF_PIN_TYPE_INPUT);
if (sroute->dst_queue_id < 0) {
dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
sink_widget->widget->name);
sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
- SOF_PIN_TYPE_SOURCE);
+ SOF_PIN_TYPE_OUTPUT);
return sroute->dst_queue_id;
}
@@ -1852,7 +2405,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
msg.primary = header;
msg.extension = extension;
- ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
if (ret < 0) {
dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
@@ -1863,8 +2416,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
return ret;
out:
- sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
- sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
+ sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
+ sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
return ret;
}
@@ -1875,9 +2428,17 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
struct sof_ipc4_msg msg = {{ 0 }};
+ struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
+ struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
+ struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
u32 header, extension;
int ret = 0;
+ /* no route is set up if chain DMA is used */
+ if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma)
+ return 0;
+
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
@@ -1903,14 +2464,14 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
msg.primary = header;
msg.extension = extension;
- ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
if (ret < 0)
dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
out:
- sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
- sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
+ sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
+ sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
return ret;
}
@@ -1939,6 +2500,11 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_HDA:
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
+ break;
+ }
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
pipeline->skip_during_fe_trigger = true;
@@ -2139,10 +2705,9 @@ static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link
static enum sof_tokens common_copier_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
SOF_OUT_AUDIO_FORMAT_TOKENS,
- SOF_COPIER_GATEWAY_CFG_TOKENS,
+ SOF_COPIER_DEEP_BUFFER_TOKENS,
SOF_COPIER_TOKENS,
SOF_COMP_EXT_TOKENS,
};
@@ -2155,10 +2720,8 @@ static enum sof_tokens pipeline_token_list[] = {
static enum sof_tokens dai_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
SOF_OUT_AUDIO_FORMAT_TOKENS,
- SOF_COPIER_GATEWAY_CFG_TOKENS,
SOF_COPIER_TOKENS,
SOF_DAI_TOKENS,
SOF_COMP_EXT_TOKENS,
@@ -2168,8 +2731,8 @@ static enum sof_tokens pga_token_list[] = {
SOF_COMP_TOKENS,
SOF_GAIN_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
+ SOF_OUT_AUDIO_FORMAT_TOKENS,
SOF_COMP_EXT_TOKENS,
};
@@ -2177,7 +2740,7 @@ static enum sof_tokens mixer_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
+ SOF_OUT_AUDIO_FORMAT_TOKENS,
SOF_COMP_EXT_TOKENS,
};
@@ -2186,7 +2749,15 @@ static enum sof_tokens src_token_list[] = {
SOF_SRC_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
+ SOF_OUT_AUDIO_FORMAT_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+};
+
+static enum sof_tokens process_token_list[] = {
+ SOF_COMP_TOKENS,
+ SOF_AUDIO_FMT_NUM_TOKENS,
+ SOF_IN_AUDIO_FORMAT_TOKENS,
+ SOF_OUT_AUDIO_FORMAT_TOKENS,
SOF_COMP_EXT_TOKENS,
};
@@ -2227,6 +2798,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
src_token_list, ARRAY_SIZE(src_token_list),
NULL, sof_ipc4_prepare_src_module,
NULL},
+ [snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process,
+ sof_ipc4_widget_free_comp_process,
+ process_token_list, ARRAY_SIZE(process_token_list),
+ NULL, sof_ipc4_prepare_process_module,
+ NULL},
};
const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 123f1096f326..cf007282867b 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -26,6 +26,10 @@
#define SOF_IPC4_MODULE_LL BIT(5)
#define SOF_IPC4_MODULE_DP BIT(6)
#define SOF_IPC4_MODULE_LIB_CODE BIT(7)
+#define SOF_IPC4_MODULE_INIT_CONFIG_MASK GENMASK(11, 8)
+
+#define SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG 0
+#define SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT 1
#define SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE 12
#define SOF_IPC4_PIPELINE_OBJECT_SIZE 448
@@ -55,6 +59,9 @@
#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
+/* FW requires minimum 2ms DMA buffer size */
+#define SOF_IPC4_MIN_DMA_BUFFER_SIZE 2
+
/*
* The base of multi-gateways. Multi-gateways addressing starts from
* ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
@@ -117,7 +124,9 @@ struct sof_ipc4_copier_config_set_sink_format {
* @priority: Priority of this pipeline
* @lp_mode: Low power mode
* @mem_usage: Memory usage
+ * @core_id: Target core for the pipeline
* @state: Pipeline state
+ * @use_chain_dma: flag to indicate if the firmware shall use chained DMA
* @msg: message structure for pipeline
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
*/
@@ -125,7 +134,9 @@ struct sof_ipc4_pipeline {
uint32_t priority;
uint32_t lp_mode;
uint32_t mem_usage;
+ uint32_t core_id;
int state;
+ bool use_chain_dma;
struct sof_ipc4_msg msg;
bool skip_during_fe_trigger;
};
@@ -141,19 +152,32 @@ struct ipc4_pipeline_set_state_data {
} __packed;
/**
+ * struct sof_ipc4_pin_format - Module pin format
+ * @pin_index: pin index
+ * @buffer_size: buffer size in bytes
+ * @audio_fmt: audio format for the pin
+ *
+ * This structure can be used for both output or input pins and the pin_index is relative to the
+ * pin type i.e output/input pin
+ */
+struct sof_ipc4_pin_format {
+ u32 pin_index;
+ u32 buffer_size;
+ struct sof_ipc4_audio_format audio_fmt;
+};
+
+/**
* struct sof_ipc4_available_audio_format - Available audio formats
- * @base_config: Available base config
- * @out_audio_fmt: Available output audio format
- * @ref_audio_fmt: Reference audio format to match runtime audio format
- * @dma_buffer_size: Available Gateway DMA buffer size (in bytes)
- * @audio_fmt_num: Number of available audio formats
+ * @output_pin_fmts: Available output pin formats
+ * @input_pin_fmts: Available input pin formats
+ * @num_input_formats: Number of input pin formats
+ * @num_output_formats: Number of output pin formats
*/
struct sof_ipc4_available_audio_format {
- struct sof_ipc4_base_module_cfg *base_config;
- struct sof_ipc4_audio_format *out_audio_fmt;
- struct sof_ipc4_audio_format *ref_audio_fmt;
- u32 *dma_buffer_size;
- int audio_fmt_num;
+ struct sof_ipc4_pin_format *output_pin_fmts;
+ struct sof_ipc4_pin_format *input_pin_fmts;
+ u32 num_input_formats;
+ u32 num_output_formats;
};
/**
@@ -266,8 +290,8 @@ struct sof_ipc4_control_data {
int index;
union {
- struct sof_ipc4_ctrl_value_chan chanv[0];
- struct sof_abi_hdr data[0];
+ DECLARE_FLEX_ARRAY(struct sof_ipc4_ctrl_value_chan, chanv);
+ DECLARE_FLEX_ARRAY(struct sof_abi_hdr, data);
};
};
@@ -329,4 +353,45 @@ struct sof_ipc4_src {
struct sof_ipc4_msg msg;
};
+/**
+ * struct sof_ipc4_base_module_cfg_ext - base module config extension containing the pin format
+ * information for the module. Both @num_input_pin_fmts and @num_output_pin_fmts cannot be 0 for a
+ * module.
+ * @num_input_pin_fmts: number of input pin formats in the @pin_formats array
+ * @num_output_pin_fmts: number of output pin formats in the @pin_formats array
+ * @reserved: reserved for future use
+ * @pin_formats: flexible array consisting of @num_input_pin_fmts input pin format items followed
+ * by @num_output_pin_fmts output pin format items
+ */
+struct sof_ipc4_base_module_cfg_ext {
+ u16 num_input_pin_fmts;
+ u16 num_output_pin_fmts;
+ u8 reserved[12];
+ DECLARE_FLEX_ARRAY(struct sof_ipc4_pin_format, pin_formats);
+} __packed;
+
+/**
+ * struct sof_ipc4_process - process config data
+ * @base_config: IPC base config data
+ * @base_config_ext: Base config extension data for module init
+ * @output_format: Output audio format
+ * @available_fmt: Available audio format
+ * @ipc_config_data: Process module config data
+ * @ipc_config_size: Size of process module config data
+ * @msg: IPC4 message struct containing header and data info
+ * @base_config_ext_size: Size of the base config extension data in bytes
+ * @init_config: Module init config type (SOF_IPC4_MODULE_INIT_CONFIG_TYPE_*)
+ */
+struct sof_ipc4_process {
+ struct sof_ipc4_base_module_cfg base_config;
+ struct sof_ipc4_base_module_cfg_ext *base_config_ext;
+ struct sof_ipc4_audio_format output_format;
+ struct sof_ipc4_available_audio_format available_fmt;
+ void *ipc_config_data;
+ uint32_t ipc_config_size;
+ struct sof_ipc4_msg msg;
+ u32 base_config_ext_size;
+ u32 init_config;
+};
+
#endif
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 8ede4b952997..246b56d24a6f 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -405,6 +405,9 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
size_t payload_bytes, bool set)
{
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
size_t payload_limit = sdev->ipc->max_payload_size;
struct sof_ipc4_msg *ipc4_msg = data;
struct sof_ipc4_msg tx = {{ 0 }};
@@ -435,6 +438,11 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data,
tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+ /* ensure the DSP is in D0i0 before sending IPC */
+ ret = snd_sof_dsp_set_power_state(sdev, &target_state);
+ if (ret < 0)
+ return ret;
+
/* Serialise IPC TX */
mutex_lock(&sdev->ipc->tx_mutex);
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 81d202e5ce53..2f8555f11c03 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -123,7 +123,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
"fw_version", 0444);
/* errors are only due to memory allocation, not debugfs */
if (ret < 0) {
- dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
+ dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n");
return ret;
}
}
@@ -131,7 +131,7 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev)
/* perform pre fw run operations */
ret = snd_sof_dsp_pre_fw_run(sdev);
if (ret < 0) {
- dev_err(sdev->dev, "error: failed pre fw run op\n");
+ dev_err(sdev->dev, "failed pre fw run op\n");
return ret;
}
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 445acb5c3a21..567db32173a8 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -211,16 +211,22 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
spcm->pcm.pcm_id, substream->stream);
- /* free PCM in the DSP */
- if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
- ret = pcm_ops->hw_free(component, substream);
- if (ret < 0)
- err = ret;
+ if (spcm->prepared[substream->stream]) {
+ /* stop DMA first if needed */
+ if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+ snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
+
+ /* free PCM in the DSP */
+ if (pcm_ops && pcm_ops->hw_free) {
+ ret = pcm_ops->hw_free(component, substream);
+ if (ret < 0)
+ err = ret;
+ }
spcm->prepared[substream->stream] = false;
}
- /* stop DMA */
+ /* reset DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0) {
dev_err(component->dev, "error: platform hw free failed\n");
@@ -301,6 +307,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
ipc_first = true;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (pcm_ops && pcm_ops->ipc_first_on_start)
+ ipc_first = true;
break;
case SNDRV_PCM_TRIGGER_START:
if (spcm->stream[substream->stream].suspend_ignored) {
@@ -312,6 +320,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = false;
return 0;
}
+
+ if (pcm_ops && pcm_ops->ipc_first_on_start)
+ ipc_first = true;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
if (sdev->system_suspend_target == SOF_SUSPEND_S0IX &&
@@ -325,29 +336,45 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
+
+ /* On suspend the DMA must be stopped in DSPless mode */
+ if (sdev->dspless_mode_selected)
+ reset_hw_params = true;
+
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
ipc_first = true;
- reset_hw_params = true;
+ if (pcm_ops && pcm_ops->reset_hw_params_during_stop)
+ reset_hw_params = true;
break;
default:
dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
return -EINVAL;
}
- /*
- * DMA and IPC sequence is different for start and stop. Need to send
- * STOP IPC before stop DMA
- */
if (!ipc_first)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
if (pcm_ops && pcm_ops->trigger)
ret = pcm_ops->trigger(component, substream, cmd);
- /* need to STOP DMA even if trigger IPC failed */
- if (ipc_first)
- snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_START:
+ /* invoke platform trigger to start DMA only if pcm_ops is successful */
+ if (ipc_first && !ret)
+ snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */
+ if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free))
+ snd_sof_pcm_platform_trigger(sdev, substream, cmd);
+ break;
+ default:
+ break;
+ }
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
if (!ret && reset_hw_params)
@@ -690,7 +717,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->pcm_construct = sof_pcm_new;
pd->ignore_machine = drv_name;
- pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
pd->be_pcm_base = SOF_BE_PCM_BASE;
pd->use_dai_pcm_id = true;
pd->topology_name_prefix = "sof";
@@ -699,4 +725,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->module_get_upon_open = 1;
pd->legacy_dai_naming = 1;
+
+ /*
+ * The fixup is only needed when the DSP is in use as with the DSPless
+ * mode we are directly using the audio interface
+ */
+ if (!sdev->dspless_mode_selected)
+ pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
}
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8d3383085d12..2fdbc53ca715 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -103,6 +103,11 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
+ if (sdev->dspless_mode_selected) {
+ sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+ return 0;
+ }
+
/*
* Nothing further to be done for platforms that support the low power
* D0 substate. Resume trace and return when resuming from
@@ -183,6 +188,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
pm_message_t pm_state;
u32 target_state = snd_sof_dsp_power_target(sdev);
+ u32 old_state = sdev->dsp_power_state.state;
int ret;
/* do nothing if dsp suspend callback is not set */
@@ -192,7 +198,12 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
return 0;
- if (tplg_ops && tplg_ops->tear_down_all_pipelines)
+ /* we need to tear down pipelines only if the DSP hardware is
+ * active, which happens for PCI devices. if the device is
+ * suspended, it is brought back to full power and then
+ * suspended again
+ */
+ if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0))
tplg_ops->tear_down_all_pipelines(sdev, false);
if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 6de388a8d0b8..1cbda595c518 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -280,9 +280,11 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget_list *list, int dir)
{
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_dapm_widget *widget;
+ struct snd_sof_route *sroute;
struct snd_soc_dapm_path *p;
- int ret;
+ int ret = 0;
int i;
/*
@@ -325,6 +327,63 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
}
}
+ /*
+ * The above loop handles connections between widgets that belong to the DAPM widget list.
+ * This is not sufficient to handle loopback cases between pipelines configured with
+ * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker
+ * protection module.
+ */
+ list_for_each_entry(sroute, &sdev->route_list, list) {
+ bool src_widget_in_dapm_list, sink_widget_in_dapm_list;
+ struct snd_sof_widget *swidget;
+
+ if (sroute->setup)
+ continue;
+
+ src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);
+ sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);
+
+ /*
+ * if both source and sink are in the DAPM list, the route must already have been
+ * set up above. And if neither are in the DAPM list, the route shouldn't be
+ * handled now.
+ */
+ if (src_widget_in_dapm_list == sink_widget_in_dapm_list)
+ continue;
+
+ /*
+ * At this point either the source widget or the sink widget is in the DAPM list
+ * with a route that might need to be set up. Check the use_count of the widget
+ * that is not in the DAPM list to confirm if it is in use currently before setting
+ * up the route.
+ */
+ if (src_widget_in_dapm_list)
+ swidget = sroute->sink_widget;
+ else
+ swidget = sroute->src_widget;
+
+ mutex_lock(&swidget->setup_mutex);
+ if (!swidget->use_count) {
+ mutex_unlock(&swidget->setup_mutex);
+ continue;
+ }
+
+ if (tplg_ops && tplg_ops->route_setup) {
+ /*
+ * this route will get freed when either the source widget or the sink
+ * widget is freed during hw_free
+ */
+ ret = tplg_ops->route_setup(sdev, sroute);
+ if (!ret)
+ sroute->setup = true;
+ }
+
+ mutex_unlock(&swidget->setup_mutex);
+
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -629,7 +688,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_sof_widget *pipe_widget;
struct snd_sof_pipeline *spipe;
- if (!swidget)
+ if (!swidget || sdev->dspless_mode_selected)
continue;
spipe = swidget->spipe;
@@ -746,16 +805,22 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs
const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
- /* Send PCM_FREE IPC to reset pipeline */
- if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
- ret = pcm_ops->hw_free(sdev->component, substream);
- if (ret < 0)
- return ret;
- }
+ if (spcm->prepared[substream->stream]) {
+ /* stop DMA first if needed */
+ if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+ snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
- spcm->prepared[substream->stream] = false;
+ /* Send PCM_FREE IPC to reset pipeline */
+ if (pcm_ops && pcm_ops->hw_free) {
+ ret = pcm_ops->hw_free(sdev->component, substream);
+ if (ret < 0)
+ return ret;
+ }
+
+ spcm->prepared[substream->stream] = false;
+ }
- /* stop the DMA */
+ /* reset the DMA */
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
if (ret < 0)
return ret;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index e0579af9d281..a090a9eb4828 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -30,9 +30,9 @@
*/
#define SOF_WIDGET_MAX_NUM_PINS 8
-/* The type of a widget pin is either sink or source */
-#define SOF_PIN_TYPE_SINK 0
-#define SOF_PIN_TYPE_SOURCE 1
+/* Widget pin type */
+#define SOF_PIN_TYPE_INPUT 0
+#define SOF_PIN_TYPE_OUTPUT 1
/* max number of FE PCMs before BEs */
#define SOF_BE_PCM_BASE 16
@@ -104,6 +104,15 @@ struct snd_sof_dai_config_data {
* @pcm_free: Function pointer for PCM free that can be used for freeing any
* additional memory in the SOF PCM stream structure
* @delay: Function pointer for pcm delay calculation
+ * @reset_hw_params_during_stop: Flag indicating whether the hw_params should be reset during the
+ * STOP pcm trigger
+ * @ipc_first_on_start: Send IPC before invoking platform trigger during
+ * START/PAUSE_RELEASE triggers
+ * @platform_stop_during_hw_free: Invoke the platform trigger during hw_free. This is needed for
+ * IPC4 where a pipeline is only paused during stop/pause/suspend
+ * triggers. The FW keeps the host DMA running in this case and
+ * therefore the host must do the same and should stop the DMA during
+ * hw_free.
*/
struct sof_ipc_pcm_ops {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -117,6 +126,9 @@ struct sof_ipc_pcm_ops {
void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
+ bool reset_hw_params_during_stop;
+ bool ipc_first_on_start;
+ bool platform_stop_during_hw_free;
};
/**
@@ -256,8 +268,7 @@ enum sof_tokens {
SOF_COMP_EXT_TOKENS,
SOF_IN_AUDIO_FORMAT_TOKENS,
SOF_OUT_AUDIO_FORMAT_TOKENS,
- SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
- SOF_COPIER_GATEWAY_CFG_TOKENS,
+ SOF_COPIER_DEEP_BUFFER_TOKENS,
SOF_COPIER_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
SOF_COPIER_FORMAT_TOKENS,
@@ -433,31 +444,31 @@ struct snd_sof_widget {
struct snd_sof_tuple *tuples;
/*
- * The allowed range for num_sink/source_pins is [0, SOF_WIDGET_MAX_NUM_PINS].
- * Widgets may have zero sink or source pins, for example the tone widget has
- * zero sink pins.
+ * The allowed range for num_input/output_pins is [0, SOF_WIDGET_MAX_NUM_PINS].
+ * Widgets may have zero input or output pins, for example the tone widget has
+ * zero input pins.
*/
- u32 num_sink_pins;
- u32 num_source_pins;
+ u32 num_input_pins;
+ u32 num_output_pins;
/*
- * The sink/source pin binding array, it takes the form of
+ * The input/output pin binding array, it takes the form of
* [widget_name_connected_to_pin0, widget_name_connected_to_pin1, ...],
* with the index as the queue ID.
*
* The array is used for special pin binding. Note that even if there
- * is only one sink/source pin requires special pin binding, pin binding
- * should be defined for all sink/source pins in topology, for pin(s) that
+ * is only one input/output pin requires special pin binding, pin binding
+ * should be defined for all input/output pins in topology, for pin(s) that
* are not used, give the value "NotConnected".
*
* If pin binding is not defined in topology, nothing to parse in the kernel,
- * sink_pin_binding and src_pin_binding shall be NULL.
+ * input_pin_binding and output_pin_binding shall be NULL.
*/
- char **sink_pin_binding;
- char **src_pin_binding;
+ char **input_pin_binding;
+ char **output_pin_binding;
- struct ida src_queue_ida;
- struct ida sink_queue_ida;
+ struct ida output_queue_ida;
+ struct ida input_queue_ida;
void *private; /* core does not touch this */
};
@@ -502,6 +513,8 @@ struct snd_sof_dai {
int number_configs;
int current_config;
struct list_head list; /* list in sdev dai list */
+ /* core should not touch this */
+ const void *platform_private;
void *private;
};
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index 4bdecd80248a..c0d6723aed59 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -64,7 +64,6 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev,
struct sof_ipc_flood_priv *priv = cdev->data;
struct device *dev = &cdev->auxdev.dev;
struct sof_ipc_cmd_hdr hdr;
- struct sof_ipc_reply reply;
u64 min_response_time = U64_MAX;
ktime_t start, end, test_end;
u64 avg_response_time = 0;
@@ -84,7 +83,7 @@ static int sof_debug_ipc_flood_test(struct sof_client_dev *cdev,
/* send test IPC's */
while (1) {
start = ktime_get();
- ret = sof_client_ipc_tx_message(cdev, &hdr, &reply, sizeof(reply));
+ ret = sof_client_ipc_tx_message_no_reply(cdev, &hdr);
end = ktime_get();
if (ret < 0)
diff --git a/sound/soc/sof/sof-client-probes-ipc3.c b/sound/soc/sof/sof-client-probes-ipc3.c
index ef768db5f04d..5e8eb19582a8 100644
--- a/sound/soc/sof/sof-client-probes-ipc3.c
+++ b/sound/soc/sof/sof-client-probes-ipc3.c
@@ -65,7 +65,6 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
{
struct sof_ipc_probe_dma_add_params *msg;
size_t size = struct_size(msg, dma, 1);
- struct sof_ipc_reply reply;
int ret;
msg = kmalloc(size, GFP_KERNEL);
@@ -77,7 +76,7 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
msg->dma[0].stream_tag = stream_tag;
msg->dma[0].dma_buffer_size = buffer_size;
- ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
kfree(msg);
return ret;
}
@@ -93,12 +92,11 @@ static int ipc3_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
static int ipc3_probes_deinit(struct sof_client_dev *cdev)
{
struct sof_ipc_cmd_hdr msg;
- struct sof_ipc_reply reply;
msg.size = sizeof(msg);
msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
- return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
+ return sof_client_ipc_tx_message_no_reply(cdev, &msg);
}
static int ipc3_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
@@ -180,7 +178,6 @@ static int ipc3_probes_points_add(struct sof_client_dev *cdev,
{
struct sof_ipc_probe_point_add_params *msg;
size_t size = struct_size(msg, desc, num_desc);
- struct sof_ipc_reply reply;
int ret;
msg = kmalloc(size, GFP_KERNEL);
@@ -191,7 +188,7 @@ static int ipc3_probes_points_add(struct sof_client_dev *cdev,
msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
memcpy(&msg->desc[0], desc, size - sizeof(*msg));
- ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
kfree(msg);
return ret;
}
@@ -211,7 +208,6 @@ static int ipc3_probes_points_remove(struct sof_client_dev *cdev,
{
struct sof_ipc_probe_point_remove_params *msg;
size_t size = struct_size(msg, buffer_id, num_buffer_id);
- struct sof_ipc_reply reply;
int ret;
msg = kmalloc(size, GFP_KERNEL);
@@ -222,7 +218,7 @@ static int ipc3_probes_points_remove(struct sof_client_dev *cdev,
msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
- ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
+ ret = sof_client_ipc_tx_message_no_reply(cdev, msg);
kfree(msg);
return ret;
}
diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c
index 66fa7c2f390a..ea21ef176c42 100644
--- a/sound/soc/sof/sof-client-probes-ipc4.c
+++ b/sound/soc/sof/sof-client-probes-ipc4.c
@@ -129,7 +129,7 @@ static int ipc4_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
msg.data_size = sizeof(cfg);
msg.data_ptr = &cfg;
- return sof_client_ipc_tx_message(cdev, &msg, NULL, 0);
+ return sof_client_ipc_tx_message_no_reply(cdev, &msg);
}
/**
@@ -156,7 +156,7 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev)
msg.data_size = 0;
msg.data_ptr = NULL;
- return sof_client_ipc_tx_message(cdev, &msg, NULL, 0);
+ return sof_client_ipc_tx_message_no_reply(cdev, &msg);
}
/**
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 9017f0864cdd..d6b7caa0cf03 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -130,6 +130,9 @@ int sof_register_clients(struct snd_sof_dev *sdev)
{
int ret;
+ if (sdev->dspless_mode_selected)
+ return 0;
+
/* Register platform independent client devices */
ret = sof_register_ipc_flood_test(sdev);
if (ret) {
diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h
index 2589714eaa91..10571d1ea9a7 100644
--- a/sound/soc/sof/sof-client.h
+++ b/sound/soc/sof/sof-client.h
@@ -39,6 +39,10 @@ struct sof_client_dev {
int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
void *reply_data, size_t reply_bytes);
+static inline int sof_client_ipc_tx_message_no_reply(struct sof_client_dev *cdev, void *ipc_msg)
+{
+ return sof_client_ipc_tx_message(cdev, ipc_msg, NULL, 0);
+}
int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg,
bool set);
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 5f919162a555..cd4f6ac126ec 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -48,6 +48,7 @@ struct snd_sof_pcm_stream;
#define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related
* configurations
*/
+#define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */
/* Flag definitions used for controlling the DSP dump behavior */
#define SOF_DBG_DUMP_REGS BIT(0)
@@ -528,6 +529,16 @@ struct snd_sof_dev {
spinlock_t ipc_lock; /* lock for IPC users */
spinlock_t hw_lock; /* lock for HW IO access */
+ /*
+ * When true the DSP is not used.
+ * It is set under the following condition:
+ * User sets the SOF_DBG_DSPLESS_MODE flag in sof_debug module parameter
+ * and
+ * the platform advertises that it can support such mode
+ * pdata->desc->dspless_mode_supported is true.
+ */
+ bool dspless_mode_selected;
+
/* Main, Base firmware image */
struct sof_firmware basefw;
@@ -700,10 +711,20 @@ static inline void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
}
int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes);
+static inline int sof_ipc_tx_message_no_reply(struct snd_sof_ipc *ipc, void *msg_data,
+ size_t msg_bytes)
+{
+ return sof_ipc_tx_message(ipc, msg_data, msg_bytes, NULL, 0);
+}
int sof_ipc_set_get_data(struct snd_sof_ipc *ipc, void *msg_data,
size_t msg_bytes, bool set);
int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes);
+static inline int sof_ipc_tx_message_no_pm_no_reply(struct snd_sof_ipc *ipc, void *msg_data,
+ size_t msg_bytes)
+{
+ return sof_ipc_tx_message_no_pm(ipc, msg_data, msg_bytes, NULL, 0);
+}
int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
size_t reply_bytes);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 9f3a038fe21a..d3d536b0a8f5 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -416,19 +416,19 @@ static const struct sof_topology_token led_tokens[] = {
};
static const struct sof_topology_token comp_pin_tokens[] = {
- {SOF_TKN_COMP_NUM_SINK_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct snd_sof_widget, num_sink_pins)},
- {SOF_TKN_COMP_NUM_SOURCE_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
- offsetof(struct snd_sof_widget, num_source_pins)},
+ {SOF_TKN_COMP_NUM_INPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, num_input_pins)},
+ {SOF_TKN_COMP_NUM_OUTPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct snd_sof_widget, num_output_pins)},
};
-static const struct sof_topology_token comp_sink_pin_binding_tokens[] = {
- {SOF_TKN_COMP_SINK_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+static const struct sof_topology_token comp_input_pin_binding_tokens[] = {
+ {SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
get_token_string, 0},
};
-static const struct sof_topology_token comp_src_pin_binding_tokens[] = {
- {SOF_TKN_COMP_SRC_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+static const struct sof_topology_token comp_output_pin_binding_tokens[] = {
+ {SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
get_token_string, 0},
};
@@ -1144,8 +1144,12 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
int dir)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_widget *host_widget;
+ if (sdev->dspless_mode_selected)
+ return 0;
+
host_widget = snd_sof_find_swidget_sname(scomp,
spcm->pcm.caps[dir].name,
dir);
@@ -1231,37 +1235,43 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
continue;
case SOF_IN_AUDIO_FORMAT_TOKENS:
- case SOF_OUT_AUDIO_FORMAT_TOKENS:
- case SOF_COPIER_GATEWAY_CFG_TOKENS:
- case SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS:
- num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_AUDIO_FORMATS,
+ num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS,
swidget->tuples, swidget->num_tuples);
-
if (num_sets < 0) {
- dev_err(sdev->dev, "Invalid audio format count for %s\n",
+ dev_err(sdev->dev, "Invalid input audio format count for %s\n",
swidget->widget->name);
ret = num_sets;
goto err;
}
-
- if (num_sets > 1) {
- struct snd_sof_tuple *new_tuples;
-
- num_tuples += token_list[object_token_list[i]].count * num_sets;
- new_tuples = krealloc(swidget->tuples,
- sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
- if (!new_tuples) {
- ret = -ENOMEM;
- goto err;
- }
-
- swidget->tuples = new_tuples;
+ break;
+ case SOF_OUT_AUDIO_FORMAT_TOKENS:
+ num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS,
+ swidget->tuples, swidget->num_tuples);
+ if (num_sets < 0) {
+ dev_err(sdev->dev, "Invalid output audio format count for %s\n",
+ swidget->widget->name);
+ ret = num_sets;
+ goto err;
}
break;
default:
break;
}
+ if (num_sets > 1) {
+ struct snd_sof_tuple *new_tuples;
+
+ num_tuples += token_list[object_token_list[i]].count * num_sets;
+ new_tuples = krealloc(swidget->tuples,
+ sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
+ if (!new_tuples) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ swidget->tuples = new_tuples;
+ }
+
/* copy one set of tuples per token ID into swidget->tuples */
ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size),
object_token_list[i], num_sets, swidget->tuples,
@@ -1286,12 +1296,12 @@ static void sof_free_pin_binding(struct snd_sof_widget *swidget,
u32 num_pins;
int i;
- if (pin_type == SOF_PIN_TYPE_SINK) {
- pin_binding = swidget->sink_pin_binding;
- num_pins = swidget->num_sink_pins;
+ if (pin_type == SOF_PIN_TYPE_INPUT) {
+ pin_binding = swidget->input_pin_binding;
+ num_pins = swidget->num_input_pins;
} else {
- pin_binding = swidget->src_pin_binding;
- num_pins = swidget->num_source_pins;
+ pin_binding = swidget->output_pin_binding;
+ num_pins = swidget->num_output_pins;
}
if (pin_binding) {
@@ -1313,14 +1323,14 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
int ret;
int i;
- if (pin_type == SOF_PIN_TYPE_SINK) {
- num_pins = swidget->num_sink_pins;
- pin_binding_token = comp_sink_pin_binding_tokens;
- token_count = ARRAY_SIZE(comp_sink_pin_binding_tokens);
+ if (pin_type == SOF_PIN_TYPE_INPUT) {
+ num_pins = swidget->num_input_pins;
+ pin_binding_token = comp_input_pin_binding_tokens;
+ token_count = ARRAY_SIZE(comp_input_pin_binding_tokens);
} else {
- num_pins = swidget->num_source_pins;
- pin_binding_token = comp_src_pin_binding_tokens;
- token_count = ARRAY_SIZE(comp_src_pin_binding_tokens);
+ num_pins = swidget->num_output_pins;
+ pin_binding_token = comp_output_pin_binding_tokens;
+ token_count = ARRAY_SIZE(comp_output_pin_binding_tokens);
}
memset(pin_binding, 0, SOF_WIDGET_MAX_NUM_PINS * sizeof(char *));
@@ -1337,10 +1347,10 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
ret = -ENOMEM;
goto err;
}
- if (pin_type == SOF_PIN_TYPE_SINK)
- swidget->sink_pin_binding = pb;
+ if (pin_type == SOF_PIN_TYPE_INPUT)
+ swidget->input_pin_binding = pb;
else
- swidget->src_pin_binding = pb;
+ swidget->output_pin_binding = pb;
}
return 0;
@@ -1379,8 +1389,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->private = NULL;
mutex_init(&swidget->setup_mutex);
- ida_init(&swidget->src_queue_ida);
- ida_init(&swidget->sink_queue_ida);
+ ida_init(&swidget->output_queue_ida);
+ ida_init(&swidget->input_queue_ida);
ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens,
ARRAY_SIZE(comp_pin_tokens), priv->array,
@@ -1391,29 +1401,29 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
goto widget_free;
}
- if (swidget->num_sink_pins > SOF_WIDGET_MAX_NUM_PINS ||
- swidget->num_source_pins > SOF_WIDGET_MAX_NUM_PINS) {
- dev_err(scomp->dev, "invalid pins for %s: [sink: %d, src: %d]\n",
- swidget->widget->name, swidget->num_sink_pins, swidget->num_source_pins);
+ if (swidget->num_input_pins > SOF_WIDGET_MAX_NUM_PINS ||
+ swidget->num_output_pins > SOF_WIDGET_MAX_NUM_PINS) {
+ dev_err(scomp->dev, "invalid pins for %s: [input: %d, output: %d]\n",
+ swidget->widget->name, swidget->num_input_pins, swidget->num_output_pins);
ret = -EINVAL;
goto widget_free;
}
- if (swidget->num_sink_pins > 1) {
- ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SINK);
+ if (swidget->num_input_pins > 1) {
+ ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_INPUT);
/* on parsing error, pin binding is not allocated, nothing to free. */
if (ret < 0) {
- dev_err(scomp->dev, "failed to parse sink pin binding for %s\n",
+ dev_err(scomp->dev, "failed to parse input pin binding for %s\n",
w->name);
goto widget_free;
}
}
- if (swidget->num_source_pins > 1) {
- ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SOURCE);
+ if (swidget->num_output_pins > 1) {
+ ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_OUTPUT);
/* on parsing error, pin binding is not allocated, nothing to free. */
if (ret < 0) {
- dev_err(scomp->dev, "failed to parse source pin binding for %s\n",
+ dev_err(scomp->dev, "failed to parse output pin binding for %s\n",
w->name);
goto widget_free;
}
@@ -1422,7 +1432,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
dev_dbg(scomp->dev,
"tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n",
swidget->comp_id, w->name, swidget->id, index,
- swidget->num_sink_pins, swidget->num_source_pins,
+ swidget->num_input_pins, swidget->num_output_pins,
strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none");
widget_ops = tplg_ops ? tplg_ops->widget : NULL;
@@ -1643,11 +1653,11 @@ out:
if (widget_ops && widget_ops[swidget->id].ipc_free)
widget_ops[swidget->id].ipc_free(swidget);
- ida_destroy(&swidget->src_queue_ida);
- ida_destroy(&swidget->sink_queue_ida);
+ ida_destroy(&swidget->output_queue_ida);
+ ida_destroy(&swidget->input_queue_ida);
- sof_free_pin_binding(swidget, SOF_PIN_TYPE_SINK);
- sof_free_pin_binding(swidget, SOF_PIN_TYPE_SOURCE);
+ sof_free_pin_binding(swidget, SOF_PIN_TYPE_INPUT);
+ sof_free_pin_binding(swidget, SOF_PIN_TYPE_OUTPUT);
kfree(swidget->tuples);
@@ -2120,7 +2130,6 @@ static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- struct snd_sof_widget *swidget, *comp_swidget;
const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_sof_control *scontrol;
struct snd_sof_pipeline *spipe;
@@ -2139,37 +2148,38 @@ static int sof_complete(struct snd_soc_component *scomp)
}
}
- /*
- * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the
- * topology will be removed and all widgets will be unloaded resulting in freeing all
- * associated memories.
- */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (widget_ops && widget_ops[swidget->id].ipc_setup) {
- ret = widget_ops[swidget->id].ipc_setup(swidget);
+ /* set up the IPC structures for the pipeline widgets */
+ list_for_each_entry(spipe, &sdev->pipeline_list, list) {
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct snd_sof_widget *swidget;
+
+ /* Update the scheduler widget's IPC structure */
+ if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) {
+ ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget);
if (ret < 0) {
dev_err(sdev->dev, "failed updating IPC struct for %s\n",
- swidget->widget->name);
+ pipe_widget->widget->name);
return ret;
}
}
- }
-
- /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
- list_for_each_entry(spipe, &sdev->pipeline_list, list) {
- struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
- /*
- * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
- * for all widgets that have the same pipeline ID as the scheduler widget.
- * Skip the scheduler widgets as they have their pipeline set during widget_ready
- */
- list_for_each_entry(comp_swidget, &sdev->widget_list, list)
- if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
- comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
- ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
+ /* set the pipeline and update the IPC structure for the non scheduler widgets */
+ list_for_each_entry(swidget, &sdev->widget_list, list)
+ if (swidget->widget->id != snd_soc_dapm_scheduler &&
+ swidget->pipeline_id == pipe_widget->pipeline_id) {
+ ret = sof_set_widget_pipeline(sdev, spipe, swidget);
if (ret < 0)
return ret;
+
+ if (widget_ops && widget_ops[swidget->id].ipc_setup) {
+ ret = widget_ops[swidget->id].ipc_setup(swidget);
+ if (ret < 0) {
+ dev_err(sdev->dev,
+ "failed updating IPC struct for %s\n",
+ swidget->widget->name);
+ return ret;
+ }
+ }
}
}
@@ -2264,6 +2274,126 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
.bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops),
};
+static int snd_sof_dspless_kcontrol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static const struct snd_soc_tplg_kcontrol_ops sof_dspless_io_ops[] = {
+ {SOF_TPLG_KCTL_VOL_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+ {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+ {SOF_TPLG_KCTL_ENUM_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+ {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+};
+
+static int snd_sof_dspless_bytes_ext_get(struct snd_kcontrol *kcontrol,
+ unsigned int __user *binary_data,
+ unsigned int size)
+{
+ return 0;
+}
+
+static int snd_sof_dspless_bytes_ext_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *binary_data,
+ unsigned int size)
+{
+ return 0;
+}
+
+static const struct snd_soc_tplg_bytes_ext_ops sof_dspless_bytes_ext_ops[] = {
+ {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_bytes_ext_get, snd_sof_dspless_bytes_ext_put},
+ {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_dspless_bytes_ext_get},
+};
+
+/* external widget init - used for any driver specific init */
+static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tw)
+{
+ if (WIDGET_IS_DAI(w->id)) {
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_sof_widget *swidget;
+ struct snd_sof_dai dai;
+ int ret;
+
+ swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
+ if (!swidget)
+ return -ENOMEM;
+
+ memset(&dai, 0, sizeof(dai));
+
+ ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+ if (ret) {
+ kfree(swidget);
+ return ret;
+ }
+
+ swidget->scomp = scomp;
+ swidget->widget = w;
+ mutex_init(&swidget->setup_mutex);
+ w->dobj.private = swidget;
+ list_add(&swidget->list, &sdev->widget_list);
+ }
+
+ return 0;
+}
+
+static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
+ struct snd_soc_dobj *dobj)
+{
+ struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+
+ if (WIDGET_IS_DAI(w->id)) {
+ struct snd_sof_widget *swidget = dobj->private;
+
+ sof_disconnect_dai_widget(scomp, w);
+
+ if (!swidget)
+ return 0;
+
+ /* remove and free swidget object */
+ list_del(&swidget->list);
+ kfree(swidget);
+ }
+
+ return 0;
+}
+
+static int sof_dspless_link_load(struct snd_soc_component *scomp, int index,
+ struct snd_soc_dai_link *link,
+ struct snd_soc_tplg_link_config *cfg)
+{
+ link->platforms->name = dev_name(scomp->dev);
+
+ /* Set nonatomic property for FE dai links for FE-BE compatibility */
+ if (!link->no_pcm)
+ link->nonatomic = true;
+
+ return 0;
+}
+
+static struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
+ /* external widget init - used for any driver specific init */
+ .widget_ready = sof_dspless_widget_ready,
+ .widget_unload = sof_dspless_widget_unload,
+
+ /* FE DAI - used for any driver specific init */
+ .dai_load = sof_dai_load,
+ .dai_unload = sof_dai_unload,
+
+ /* DAI link - used for any driver specific init */
+ .link_load = sof_dspless_link_load,
+
+ /* vendor specific kcontrol handlers available for binding */
+ .io_ops = sof_dspless_io_ops,
+ .io_ops_count = ARRAY_SIZE(sof_dspless_io_ops),
+
+ /* vendor specific bytes ext handlers available for binding */
+ .bytes_ext_ops = sof_dspless_bytes_ext_ops,
+ .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops),
+};
+
int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
@@ -2281,7 +2411,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
return ret;
}
- ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+ 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);
+
if (ret < 0) {
dev_err(scomp->dev, "error: tplg component load failed %d\n",
ret);