summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/Makefile2
-rw-r--r--sound/soc/sof/compress.c9
-rw-r--r--sound/soc/sof/compress.h9
-rw-r--r--sound/soc/sof/control.c2
-rw-r--r--sound/soc/sof/core.c9
-rw-r--r--sound/soc/sof/debug.c2
-rw-r--r--sound/soc/sof/imx/Kconfig34
-rw-r--r--sound/soc/sof/imx/Makefile4
-rw-r--r--sound/soc/sof/imx/imx8.c4
-rw-r--r--sound/soc/sof/imx/imx8m.c285
-rw-r--r--sound/soc/sof/intel/Makefile2
-rw-r--r--sound/soc/sof/intel/apl.c2
-rw-r--r--sound/soc/sof/intel/bdw.c2
-rw-r--r--sound/soc/sof/intel/byt.c172
-rw-r--r--sound/soc/sof/intel/cnl.c2
-rw-r--r--sound/soc/sof/intel/hda-bus.c2
-rw-r--r--sound/soc/sof/intel/hda-codec.c58
-rw-r--r--sound/soc/sof/intel/hda-compress.c2
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c2
-rw-r--r--sound/soc/sof/intel/hda-dai.c2
-rw-r--r--sound/soc/sof/intel/hda-dsp.c10
-rw-r--r--sound/soc/sof/intel/hda-ipc.c2
-rw-r--r--sound/soc/sof/intel/hda-ipc.h2
-rw-r--r--sound/soc/sof/intel/hda-loader.c11
-rw-r--r--sound/soc/sof/intel/hda-pcm.c2
-rw-r--r--sound/soc/sof/intel/hda-stream.c2
-rw-r--r--sound/soc/sof/intel/hda-trace.c2
-rw-r--r--sound/soc/sof/intel/hda.c28
-rw-r--r--sound/soc/sof/intel/hda.h2
-rw-r--r--sound/soc/sof/intel/intel-ipc.c2
-rw-r--r--sound/soc/sof/intel/shim.h2
-rw-r--r--sound/soc/sof/ipc.c11
-rw-r--r--sound/soc/sof/loader.c213
-rw-r--r--sound/soc/sof/nocodec.c8
-rw-r--r--sound/soc/sof/ops.c2
-rw-r--r--sound/soc/sof/ops.h2
-rw-r--r--sound/soc/sof/pcm.c18
-rw-r--r--sound/soc/sof/pm.c29
-rw-r--r--sound/soc/sof/probe.c2
-rw-r--r--sound/soc/sof/probe.h2
-rw-r--r--sound/soc/sof/sof-acpi-dev.c2
-rw-r--r--sound/soc/sof/sof-audio.c2
-rw-r--r--sound/soc/sof/sof-audio.h2
-rw-r--r--sound/soc/sof/sof-of-dev.c16
-rw-r--r--sound/soc/sof/sof-pci-dev.c4
-rw-r--r--sound/soc/sof/sof-priv.h7
-rw-r--r--sound/soc/sof/topology.c483
-rw-r--r--sound/soc/sof/trace.c2
-rw-r--r--sound/soc/sof/utils.c2
-rw-r--r--sound/soc/sof/xtensa/Makefile2
-rw-r--r--sound/soc/sof/xtensa/core.c2
51 files changed, 1056 insertions, 426 deletions
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile
index 8eca2f85c90e..05718dfe6cd2 100644
--- a/sound/soc/sof/Makefile
+++ b/sound/soc/sof/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o utils.o sof-audio.o
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 7354dc6a49cf..2d4969c705a4 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -13,7 +13,7 @@
#include "ops.h"
#include "probe.h"
-struct snd_compr_ops sof_probe_compressed_ops = {
+struct snd_compress_ops sof_probe_compressed_ops = {
.copy = sof_probe_compr_copy,
};
EXPORT_SYMBOL(sof_probe_compressed_ops);
@@ -117,8 +117,9 @@ int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
}
EXPORT_SYMBOL(sof_probe_compr_pointer);
-int sof_probe_compr_copy(struct snd_compr_stream *cstream,
- char __user *buf, size_t count)
+int sof_probe_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
{
struct snd_compr_runtime *rtd = cstream->runtime;
unsigned int offset, n;
diff --git a/sound/soc/sof/compress.h b/sound/soc/sof/compress.h
index 800f163603e1..ca8790bd4b13 100644
--- a/sound/soc/sof/compress.h
+++ b/sound/soc/sof/compress.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
@@ -13,7 +13,7 @@
#include <sound/compress_driver.h>
-extern struct snd_compr_ops sof_probe_compressed_ops;
+extern struct snd_compress_ops sof_probe_compressed_ops;
int sof_probe_compr_open(struct snd_compr_stream *cstream,
struct snd_soc_dai *dai);
@@ -25,7 +25,8 @@ int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
struct snd_soc_dai *dai);
int sof_probe_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai);
-int sof_probe_compr_copy(struct snd_compr_stream *cstream,
- char __user *buf, size_t count);
+int sof_probe_compr_copy(struct snd_soc_component *component,
+ struct snd_compr_stream *cstream,
+ char __user *buf, size_t count);
#endif
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index 6d63768d42aa..186eea105bb1 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 91acfae7935c..339c4930b0c0 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -176,6 +176,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
/* init the IPC */
sdev->ipc = snd_sof_ipc_init(sdev);
if (!sdev->ipc) {
+ ret = -ENOMEM;
dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
goto ipc_err;
}
@@ -342,6 +343,12 @@ int snd_sof_device_remove(struct device *dev)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_sof_pdata *pdata = sdev->pdata;
+ int ret;
+
+ ret = snd_sof_dsp_power_down_notify(sdev);
+ if (ret < 0)
+ dev_warn(dev, "error: %d failed to prepare DSP for device removal",
+ ret);
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index b5c0d6cf72cc..8e15f105d1d5 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index bae4f7bf5f75..8230285baa43 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
config SND_SOC_SOF_IMX_TOPLEVEL
bool "SOF support for NXP i.MX audio DSPs"
@@ -11,17 +11,41 @@ config SND_SOC_SOF_IMX_TOPLEVEL
if SND_SOC_SOF_IMX_TOPLEVEL
+config SND_SOC_SOF_IMX_OF
+ def_tristate SND_SOC_SOF_OF
+ select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT
+ select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
config SND_SOC_SOF_IMX8_SUPPORT
bool "SOF support for i.MX8"
- depends on IMX_SCU
- depends on IMX_DSP
+ depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF
+ depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_IMX_OF
help
This adds support for Sound Open Firmware for NXP i.MX8 platforms
Say Y if you have such a device.
If unsure select "N".
config SND_SOC_SOF_IMX8
- def_tristate SND_SOC_SOF_OF
- depends on SND_SOC_SOF_IMX8_SUPPORT
+ tristate
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
+config SND_SOC_SOF_IMX8M_SUPPORT
+ bool "SOF support for i.MX8M"
+ depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_OF
+ help
+ This adds support for Sound Open Firmware for NXP i.MX8M platforms
+ Say Y if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_SOF_IMX8M
+ tristate
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile
index 6ef908e8c807..2b933b02bbac 100644
--- a/sound/soc/sof/imx/Makefile
+++ b/sound/soc/sof/imx/Makefile
@@ -1,4 +1,6 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-imx8-objs := imx8.o
+snd-sof-imx8m-objs := imx8m.o
obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o
+obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index b692752b2178..63f9c20a1bac 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// Copyright 2019 NXP
//
@@ -119,7 +119,7 @@ static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
snd_sof_ipc_msgs_rx(priv->sdev);
}
-struct imx_dsp_ops dsp_ops = {
+static struct imx_dsp_ops dsp_ops = {
.handle_reply = imx8_dsp_handle_reply,
.handle_request = imx8_dsp_handle_request,
};
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
new file mode 100644
index 000000000000..fa86a9e2990f
--- /dev/null
+++ b/sound/soc/sof/imx/imx8m.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// Copyright 2020 NXP
+//
+// Author: Daniel Baluta <daniel.baluta@nxp.com>
+//
+// Hardware interface for audio DSP on i.MX8M
+
+#include <linux/firmware.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <linux/module.h>
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include <linux/firmware/imx/dsp.h>
+
+#include "../ops.h"
+
+#define MBOX_OFFSET 0x800000
+#define MBOX_SIZE 0x1000
+
+struct imx8m_priv {
+ struct device *dev;
+ struct snd_sof_dev *sdev;
+
+ /* DSP IPC handler */
+ struct imx_dsp_ipc *dsp_ipc;
+ struct platform_device *ipc_dev;
+};
+
+static void imx8m_get_reply(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_ipc_msg *msg = sdev->msg;
+ struct sof_ipc_reply reply;
+ int ret = 0;
+
+ if (!msg) {
+ dev_warn(sdev->dev, "unexpected ipc interrupt\n");
+ return;
+ }
+
+ /* get reply */
+ sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
+
+ if (reply.error < 0) {
+ memcpy(msg->reply_data, &reply, sizeof(reply));
+ ret = reply.error;
+ } else {
+ /* reply has correct size? */
+ if (reply.hdr.size != msg->reply_size) {
+ dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
+ msg->reply_size, reply.hdr.size);
+ ret = -EINVAL;
+ }
+
+ /* read the message */
+ if (msg->reply_size > 0)
+ sof_mailbox_read(sdev, sdev->host_box.offset,
+ msg->reply_data, msg->reply_size);
+ }
+
+ msg->reply_error = ret;
+}
+
+static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return MBOX_OFFSET;
+}
+
+static int imx8m_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return MBOX_OFFSET;
+}
+
+static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc)
+{
+ struct imx8m_priv *priv = imx_dsp_get_data(ipc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
+ imx8m_get_reply(priv->sdev);
+ snd_sof_ipc_reply(priv->sdev, 0);
+ spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
+}
+
+static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc)
+{
+ struct imx8m_priv *priv = imx_dsp_get_data(ipc);
+
+ snd_sof_ipc_msgs_rx(priv->sdev);
+}
+
+static struct imx_dsp_ops imx8m_dsp_ops = {
+ .handle_reply = imx8m_dsp_handle_reply,
+ .handle_request = imx8m_dsp_handle_request,
+};
+
+static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
+ msg->msg_size);
+ imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
+
+ return 0;
+}
+
+/*
+ * DSP control.
+ */
+static int imx8m_run(struct snd_sof_dev *sdev)
+{
+ /* TODO: start DSP using Audio MIX bits */
+ return 0;
+}
+
+static int imx8m_probe(struct snd_sof_dev *sdev)
+{
+ struct platform_device *pdev =
+ container_of(sdev->dev, struct platform_device, dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *res_node;
+ struct resource *mmio;
+ struct imx8m_priv *priv;
+ struct resource res;
+ u32 base, size;
+ int ret = 0;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->private = priv;
+ priv->dev = sdev->dev;
+ priv->sdev = sdev;
+
+ priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(priv->ipc_dev))
+ return PTR_ERR(priv->ipc_dev);
+
+ priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
+ if (!priv->dsp_ipc) {
+ /* DSP IPC driver not probed yet, try later */
+ ret = -EPROBE_DEFER;
+ dev_err(sdev->dev, "Failed to get drvdata\n");
+ goto exit_pdev_unregister;
+ }
+
+ imx_dsp_set_data(priv->dsp_ipc, priv);
+ priv->dsp_ipc->ops = &imx8m_dsp_ops;
+
+ /* DSP base */
+ mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mmio) {
+ base = mmio->start;
+ size = resource_size(mmio);
+ } else {
+ dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
+ ret = -EINVAL;
+ goto exit_pdev_unregister;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
+ dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
+ base, size);
+ ret = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+ sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
+
+ res_node = of_parse_phandle(np, "memory-region", 0);
+ if (!res_node) {
+ dev_err(&pdev->dev, "failed to get memory region node\n");
+ ret = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+
+ ret = of_address_to_resource(res_node, 0, &res);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get reserved region address\n");
+ goto exit_pdev_unregister;
+ }
+
+ sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
+ res.end - res.start +
+ 1);
+ if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
+ dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
+ base, size);
+ ret = -ENOMEM;
+ goto exit_pdev_unregister;
+ }
+ sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = MBOX_OFFSET;
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_unregister(priv->ipc_dev);
+ return ret;
+}
+
+static int imx8m_remove(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+
+ platform_device_unregister(priv->ipc_dev);
+
+ return 0;
+}
+
+/* on i.MX8 there is 1 to 1 match between type and BAR idx */
+static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ return type;
+}
+
+static void imx8m_ipc_msg_data(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ void *p, size_t sz)
+{
+ sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
+}
+
+static int imx8m_ipc_pcm_params(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ const struct sof_ipc_pcm_params_reply *reply)
+{
+ return 0;
+}
+
+static struct snd_soc_dai_driver imx8m_dai[] = {
+{
+ .name = "sai-port",
+},
+};
+
+/* i.MX8 ops */
+struct snd_sof_dsp_ops sof_imx8m_ops = {
+ /* probe and remove */
+ .probe = imx8m_probe,
+ .remove = imx8m_remove,
+ /* DSP core boot */
+ .run = imx8m_run,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* ipc */
+ .send_msg = imx8m_send_msg,
+ .fw_ready = sof_fw_ready,
+ .get_mailbox_offset = imx8m_get_mailbox_offset,
+ .get_window_offset = imx8m_get_window_offset,
+
+ .ipc_msg_data = imx8m_ipc_msg_data,
+ .ipc_pcm_params = imx8m_ipc_pcm_params,
+
+ /* module loading */
+ .load_module = snd_sof_parse_module_memcpy,
+ .get_bar_index = imx8m_get_bar_index,
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* DAI drivers */
+ .drv = imx8m_dai,
+ .num_drv = 1, /* we have only 1 SAI interface on i.MX8M */
+
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+};
+EXPORT_SYMBOL(sof_imx8m_ops);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index cee02a2e00f4..f7e9358f1f06 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-intel-byt-objs := byt.o
snd-sof-intel-bdw-objs := bdw.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 02218d22e51f..9e29d4fd393a 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index a32a3ef78ec5..99fd0bd7276e 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 29fd1d86156c..49f67f1b94e0 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -82,33 +82,6 @@ static const struct snd_sof_debugfs_map byt_debugfs[] = {
SOF_DEBUGFS_ACCESS_ALWAYS},
};
-static const struct snd_sof_debugfs_map cht_debugfs[] = {
- {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE,
- SOF_DEBUGFS_ACCESS_ALWAYS},
- {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
- SOF_DEBUGFS_ACCESS_D0_ONLY},
- {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
- SOF_DEBUGFS_ACCESS_ALWAYS},
-};
-
static void byt_host_done(struct snd_sof_dev *sdev);
static void byt_dsp_done(struct snd_sof_dev *sdev);
static void byt_get_reply(struct snd_sof_dev *sdev);
@@ -187,13 +160,31 @@ static void byt_dump(struct snd_sof_dev *sdev, u32 flags)
static irqreturn_t byt_irq_handler(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
- u64 isr;
+ u64 ipcx, ipcd;
int ret = IRQ_NONE;
- /* Interrupt arrived, check src */
- isr = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_ISRX);
- if (isr & (SHIM_ISRX_DONE | SHIM_ISRX_BUSY))
+ ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
+
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
+
+ /* reply message from DSP, Mask Done interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
+
+ /* new message from DSP, Mask Busy interrupt first */
+ snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
+ SHIM_IMRX,
+ SHIM_IMRX_BUSY,
+ SHIM_IMRX_BUSY);
ret = IRQ_WAKE_THREAD;
+ }
return ret;
}
@@ -202,19 +193,12 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
u64 ipcx, ipcd;
- u64 imrx;
- imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX);
ipcx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
+ ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
/* reply message from DSP */
- if (ipcx & SHIM_BYT_IPCX_DONE &&
- !(imrx & SHIM_IMRX_DONE)) {
- /* Mask Done interrupt before first */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_DONE,
- SHIM_IMRX_DONE);
+ if (ipcx & SHIM_BYT_IPCX_DONE) {
spin_lock_irq(&sdev->ipc_lock);
@@ -234,14 +218,7 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
}
/* new message from DSP */
- ipcd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
- if (ipcd & SHIM_BYT_IPCD_BUSY &&
- !(imrx & SHIM_IMRX_BUSY)) {
- /* Mask Busy interrupt before return */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR,
- SHIM_IMRX,
- SHIM_IMRX_BUSY,
- SHIM_IMRX_BUSY);
+ if (ipcd & SHIM_BYT_IPCD_BUSY) {
/* Handle messages from DSP Core */
if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
@@ -259,6 +236,10 @@ static irqreturn_t byt_irq_thread(int irq, void *context)
static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
+ /* unmask and prepare to receive Done interrupt */
+ snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_DONE, 0);
+
/* send the message */
sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
msg->msg_size);
@@ -324,7 +305,7 @@ static void byt_host_done(struct snd_sof_dev *sdev)
SHIM_BYT_IPCD_DONE,
SHIM_BYT_IPCD_DONE);
- /* unmask busy interrupt */
+ /* unmask and prepare to receive next new message */
snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
SHIM_IMRX_BUSY, 0);
}
@@ -334,10 +315,6 @@ static void byt_dsp_done(struct snd_sof_dev *sdev)
/* clear DONE bit - tell DSP we have completed */
snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IPCX,
SHIM_BYT_IPCX_DONE, 0);
-
- /* unmask Done interrupt */
- snd_sof_dsp_update_bits64_unlocked(sdev, BYT_DSP_BAR, SHIM_IMRX,
- SHIM_IMRX_DONE, 0);
}
/*
@@ -594,9 +571,10 @@ irq:
return ret;
}
- /* enable Interrupt from both sides */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0);
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0);
+ /* enable BUSY and disable DONE Interrupt by default */
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = MBOX_OFFSET;
@@ -681,6 +659,69 @@ EXPORT_SYMBOL_NS(tng_chip_info, SND_SOC_SOF_MERRIFIELD);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
+static void byt_reset_dsp_disable_int(struct snd_sof_dev *sdev)
+{
+ /* Disable Interrupt from both sides */
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x3);
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x3);
+
+ /* Put DSP into reset, set reset vector */
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_CSR,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL,
+ SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL);
+}
+
+static int byt_suspend(struct snd_sof_dev *sdev, u32 target_state)
+{
+ byt_reset_dsp_disable_int(sdev);
+
+ return 0;
+}
+
+static int byt_resume(struct snd_sof_dev *sdev)
+{
+ /* enable BUSY and disable DONE Interrupt by default */
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
+
+ return 0;
+}
+
+static int byt_remove(struct snd_sof_dev *sdev)
+{
+ byt_reset_dsp_disable_int(sdev);
+
+ return 0;
+}
+
+static const struct snd_sof_debugfs_map cht_debugfs[] = {
+ {"dmac0", BYT_DSP_BAR, DMAC0_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac1", BYT_DSP_BAR, DMAC1_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"dmac2", BYT_DSP_BAR, DMAC2_OFFSET, DMAC_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp0", BYT_DSP_BAR, SSP0_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp1", BYT_DSP_BAR, SSP1_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp2", BYT_DSP_BAR, SSP2_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp3", BYT_DSP_BAR, SSP3_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp4", BYT_DSP_BAR, SSP4_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"ssp5", BYT_DSP_BAR, SSP5_OFFSET, SSP_SIZE,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+ {"iram", BYT_DSP_BAR, IRAM_OFFSET, IRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
+ SOF_DEBUGFS_ACCESS_D0_ONLY},
+ {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
+ SOF_DEBUGFS_ACCESS_ALWAYS},
+};
+
static int byt_acpi_probe(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *pdata = sdev->pdata;
@@ -769,9 +810,10 @@ irq:
return ret;
}
- /* enable Interrupt from both sides */
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX, 0x3, 0x0);
- snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRD, 0x3, 0x0);
+ /* enable BUSY and disable DONE Interrupt by default */
+ snd_sof_dsp_update_bits64(sdev, BYT_DSP_BAR, SHIM_IMRX,
+ SHIM_IMRX_BUSY | SHIM_IMRX_DONE,
+ SHIM_IMRX_DONE);
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = MBOX_OFFSET;
@@ -783,6 +825,7 @@ irq:
const struct snd_sof_dsp_ops sof_byt_ops = {
/* device init */
.probe = byt_acpi_probe,
+ .remove = byt_remove,
/* DSP core boot / reset */
.run = byt_run,
@@ -832,6 +875,10 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
+ /* PM */
+ .suspend = byt_suspend,
+ .resume = byt_resume,
+
/* DAI drivers */
.drv = byt_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
@@ -857,6 +904,7 @@ EXPORT_SYMBOL_NS(byt_chip_info, SND_SOC_SOF_BAYTRAIL);
const struct snd_sof_dsp_ops sof_cht_ops = {
/* device init */
.probe = byt_acpi_probe,
+ .remove = byt_remove,
/* DSP core boot / reset */
.run = byt_run,
@@ -906,6 +954,10 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
/*Firmware loading */
.load_firmware = snd_sof_load_firmware_memcpy,
+ /* PM */
+ .suspend = byt_suspend,
+ .resume = byt_resume,
+
/* DAI drivers */
.drv = byt_dai,
/* all 6 SSPs may be available for cherrytrail */
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index e427d00eca71..16db0f50d139 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c
index 1d2babdda9dd..789148e5584b 100644
--- a/sound/soc/sof/intel/hda-bus.c
+++ b/sound/soc/sof/intel/hda-bus.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c
index 3041fbbb010a..2c5c451fa19d 100644
--- a/sound/soc/sof/intel/hda-codec.c
+++ b/sound/soc/sof/intel/hda-codec.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -24,19 +24,44 @@
#define IDISP_VID_INTEL 0x80860000
/* load the legacy HDA codec driver */
-static int hda_codec_load_module(struct hda_codec *codec)
+static int request_codec_module(struct hda_codec *codec)
{
#ifdef MODULE
char alias[MODULE_NAME_LEN];
- const char *module = alias;
+ const char *mod = NULL;
- snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias));
- dev_dbg(&codec->core.dev, "loading codec module: %s\n", module);
- request_module(module);
+ switch (codec->probe_id) {
+ case HDA_CODEC_ID_GENERIC:
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ mod = "snd-hda-codec-generic";
#endif
+ break;
+ default:
+ snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias));
+ mod = alias;
+ break;
+ }
+
+ if (mod) {
+ dev_dbg(&codec->core.dev, "loading codec module: %s\n", mod);
+ request_module(mod);
+ }
+#endif /* MODULE */
return device_attach(hda_codec_dev(codec));
}
+static int hda_codec_load_module(struct hda_codec *codec)
+{
+ int ret = request_codec_module(codec);
+
+ if (ret <= 0) {
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ ret = request_codec_module(codec);
+ }
+
+ return ret;
+}
+
/* enable controller wake up event for all codecs with jack connectors */
void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev)
{
@@ -78,6 +103,13 @@ void hda_codec_jack_check(struct snd_sof_dev *sdev) {}
EXPORT_SYMBOL_NS(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC);
EXPORT_SYMBOL_NS(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC);
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
+#define is_generic_config(bus) \
+ ((bus)->modelname && !strcmp((bus)->modelname, "generic"))
+#else
+#define is_generic_config(x) 0
+#endif
+
/* probe individual codec */
static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
bool hda_codec_use_common_hdmi)
@@ -87,6 +119,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
#endif
struct hda_bus *hbus = sof_to_hbus(sdev);
struct hdac_device *hdev;
+ struct hda_codec *codec;
u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
u32 resp = -1;
@@ -108,6 +141,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
hda_priv->codec.bus = hbus;
hdev = &hda_priv->codec.core;
+ codec = &hda_priv->codec;
ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev);
if (ret < 0)
@@ -122,6 +156,11 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
hda_priv->need_display_power = true;
}
+ if (is_generic_config(hbus))
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ else
+ codec->probe_id = 0;
+
/*
* if common HDMI codec driver is not used, codec load
* is skipped here and hdac_hdmi is used instead
@@ -129,7 +168,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
if (hda_codec_use_common_hdmi ||
(resp & 0xFFFF0000) != IDISP_VID_INTEL) {
hdev->type = HDA_DEV_LEGACY;
- ret = hda_codec_load_module(&hda_priv->codec);
+ ret = hda_codec_load_module(codec);
/*
* handle ret==0 (no driver bound) as an error, but pass
* other return codes without modification
@@ -207,7 +246,6 @@ EXPORT_SYMBOL_NS(hda_codec_i915_init, SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
int hda_codec_i915_exit(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = sof_to_bus(sdev);
- int ret;
if (!bus->audio_component)
return 0;
@@ -215,9 +253,7 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev)
/* power down unconditionally */
snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
- ret = snd_hdac_i915_exit(bus);
-
- return ret;
+ return snd_hdac_i915_exit(bus);
}
EXPORT_SYMBOL_NS(hda_codec_i915_exit, SND_SOC_SOF_HDA_AUDIO_CODEC_I915);
diff --git a/sound/soc/sof/intel/hda-compress.c b/sound/soc/sof/intel/hda-compress.c
index 38a1ebec8478..53c08034fa22 100644
--- a/sound/soc/sof/intel/hda-compress.c
+++ b/sound/soc/sof/intel/hda-compress.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index 6288b2f99540..fa5f0a718901 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 833dc303b394..3934cd6bf87a 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 99087b6afb67..9e5ff8c18f99 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -226,10 +226,10 @@ bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev,
val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS);
- is_enable = ((val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) &&
- (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) &&
- !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
- !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)));
+ is_enable = (val & HDA_DSP_ADSPCS_CPA_MASK(core_mask)) &&
+ (val & HDA_DSP_ADSPCS_SPA_MASK(core_mask)) &&
+ !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) &&
+ !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask));
dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n",
is_enable, core_mask);
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index 6062bb6011fb..c91aa951df22 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h
index aef0ceac9803..ade4c3191a39 100644
--- a/sound/soc/sof/intel/hda-ipc.h
+++ b/sound/soc/sof/intel/hda-ipc.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index e1550ccd0a49..441d05cda604 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -293,8 +293,13 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
chip_info = desc->chip_info;
- stripped_firmware.data = plat_data->fw->data;
- stripped_firmware.size = plat_data->fw->size;
+ if (plat_data->fw->size <= plat_data->fw_offset) {
+ dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
+ return -EINVAL;
+ }
+
+ stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset;
+ stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset;
/* init for booting wait */
init_waitqueue_head(&sdev->boot_wait);
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index a46a6baa1c3f..53a875ac52d6 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 5d386956906f..7f65dcc95811 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c
index 33b23bd6a01e..1eb746d5adeb 100644
--- a/sound/soc/sof/intel/hda-trace.c
+++ b/sound/soc/sof/intel/hda-trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 211e91e79eae..63ca920c8e6e 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -135,10 +135,8 @@ static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
hdev = sdev->pdata->hw_pdata;
ret = sdw_intel_acpi_scan(handle, &hdev->info);
- if (ret < 0) {
- dev_err(sdev->dev, "%s failed\n", __func__);
+ if (ret < 0)
return -EINVAL;
- }
return 0;
}
@@ -282,6 +280,10 @@ module_param_named(use_msi, hda_use_msi, bool, 0444);
MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode");
#endif
+static char *hda_model;
+module_param(hda_model, charp, 0444);
+MODULE_PARM_DESC(hda_model, "Use the given HDA board model.");
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
static int hda_dmic_num = -1;
module_param_named(dmic_num, hda_dmic_num, int, 0444);
@@ -503,7 +505,7 @@ static int hda_init(struct snd_sof_dev *sdev)
mutex_init(&hbus->prepare_mutex);
hbus->pci = pci;
hbus->mixer_assigned = -1;
- hbus->modelname = "sofbus";
+ hbus->modelname = hda_model;
/* initialise hdac bus */
bus->addr = pci_resource_start(pci, 0);
@@ -604,7 +606,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
/* scan SoundWire capabilities exposed by DSDT */
ret = hda_sdw_acpi_scan(sdev);
if (ret < 0) {
- dev_dbg(sdev->dev, "skipping SoundWire, ACPI scan error\n");
+ dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n");
goto skip_soundwire;
}
@@ -1008,6 +1010,10 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev)
if (!tplg_filename)
return -EINVAL;
+ dev_info(bus->dev,
+ "DMICs detected in NHLT tables: %d\n",
+ dmic_num);
+
pdata->machine = hda_mach;
pdata->tplg_filename = tplg_filename;
}
@@ -1101,7 +1107,15 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev)
if (link_mask && !pdata->machine) {
for (mach = pdata->desc->alt_machines;
mach && mach->link_mask; mach++) {
- if (mach->link_mask != link_mask)
+ /*
+ * On some platforms such as Up Extreme all links
+ * are enabled but only one link can be used by
+ * external codec. Instead of exact match of two masks,
+ * first check whether link_mask of mach is subset of
+ * link_mask supported by hw and then go on searching
+ * link_adr
+ */
+ if (~link_mask & mach->link_mask)
continue;
/* No need to match adr if there is no links defined */
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index e9825798de77..fe452f0d0ec7 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/intel/intel-ipc.c b/sound/soc/sof/intel/intel-ipc.c
index e935f70d611b..310f9168c124 100644
--- a/sound/soc/sof/intel/intel-ipc.c
+++ b/sound/soc/sof/intel/intel-ipc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h
index daaf3364c177..6fe8b004b50e 100644
--- a/sound/soc/sof/intel/shim.h
+++ b/sound/soc/sof/intel/shim.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c
index 1c6794918cbb..36e2d4d43da4 100644
--- a/sound/soc/sof/ipc.c
+++ b/sound/soc/sof/ipc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -335,21 +335,20 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
/* handle reply message from DSP */
-int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
+void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
if (msg->ipc_complete) {
- dev_err(sdev->dev, "error: no reply expected, received 0x%x",
+ dev_dbg(sdev->dev,
+ "no reply expected, received 0x%x, will be ignored",
msg_id);
- return -EINVAL;
+ return;
}
/* wake up and return the error if we have waiters on this message ? */
msg->ipc_complete = true;
wake_up(&msg->waitq);
-
- return 0;
}
EXPORT_SYMBOL(snd_sof_ipc_reply);
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 64af08293daa..b94fa5f5d480 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -12,20 +12,29 @@
#include <linux/firmware.h>
#include <sound/sof.h>
+#include <sound/sof/ext_manifest.h>
#include "ops.h"
static int get_ext_windows(struct snd_sof_dev *sdev,
- struct sof_ipc_ext_data_hdr *ext_hdr)
+ const struct sof_ipc_ext_data_hdr *ext_hdr)
{
- struct sof_ipc_window *w =
+ const struct sof_ipc_window *w =
container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
+ size_t w_size = struct_size(w, window, w->num_windows);
if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
return -EINVAL;
+ if (sdev->info_window) {
+ if (memcmp(sdev->info_window, w, w_size)) {
+ dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
/* keep a local copy of the data */
- sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows),
- GFP_KERNEL);
+ sdev->info_window = kmemdup(w, w_size, GFP_KERNEL);
if (!sdev->info_window)
return -ENOMEM;
@@ -33,13 +42,21 @@ static int get_ext_windows(struct snd_sof_dev *sdev,
}
static int get_cc_info(struct snd_sof_dev *sdev,
- struct sof_ipc_ext_data_hdr *ext_hdr)
+ const struct sof_ipc_ext_data_hdr *ext_hdr)
{
int ret;
- struct sof_ipc_cc_version *cc =
+ const struct sof_ipc_cc_version *cc =
container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
+ if (sdev->cc_version) {
+ if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
+ dev_err(sdev->dev, "error: receive diverged cc_version descriptions");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
cc->name, cc->major, cc->minor, cc->micro, cc->desc,
cc->optim);
@@ -126,6 +143,142 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
}
EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
+static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_fw_version *v =
+ container_of(hdr, struct sof_ext_man_fw_version, hdr);
+
+ memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
+ sdev->fw_ready.flags = v->flags;
+
+ /* log ABI versions and check FW compatibility */
+ return snd_sof_ipc_valid(sdev);
+}
+
+static int ext_man_get_windows(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_window *w;
+
+ w = container_of(hdr, struct sof_ext_man_window, hdr);
+
+ return get_ext_windows(sdev, &w->ipc_window.ext_hdr);
+}
+
+static int ext_man_get_cc_info(struct snd_sof_dev *sdev,
+ const struct sof_ext_man_elem_header *hdr)
+{
+ const struct sof_ext_man_cc_version *cc;
+
+ cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
+
+ return get_cc_info(sdev, &cc->cc_version.ext_hdr);
+}
+
+static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
+{
+ const struct sof_ext_man_header *head;
+
+ head = (struct sof_ext_man_header *)fw->data;
+
+ /*
+ * assert fw size is big enough to contain extended manifest header,
+ * it prevents from reading unallocated memory from `head` in following
+ * step.
+ */
+ if (fw->size < sizeof(*head))
+ return -EINVAL;
+
+ /*
+ * When fw points to extended manifest,
+ * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
+ */
+ if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
+ return head->full_size;
+
+ /* otherwise given fw don't have an extended manifest */
+ return 0;
+}
+
+/* parse extended FW manifest data structures */
+static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev,
+ const struct firmware *fw)
+{
+ const struct sof_ext_man_elem_header *elem_hdr;
+ const struct sof_ext_man_header *head;
+ ssize_t ext_man_size;
+ ssize_t remaining;
+ uintptr_t iptr;
+ int ret = 0;
+
+ head = (struct sof_ext_man_header *)fw->data;
+ remaining = head->full_size - head->header_size;
+ ext_man_size = snd_sof_ext_man_size(fw);
+
+ /* Assert firmware starts with extended manifest */
+ if (ext_man_size <= 0)
+ return ext_man_size;
+
+ /* incompatible version */
+ if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
+ head->header_version)) {
+ dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n",
+ head->header_version, SOF_EXT_MAN_VERSION);
+ return -EINVAL;
+ }
+
+ /* get first extended manifest element header */
+ iptr = (uintptr_t)fw->data + head->header_size;
+
+ while (remaining > sizeof(*elem_hdr)) {
+ elem_hdr = (struct sof_ext_man_elem_header *)iptr;
+
+ dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n",
+ elem_hdr->type, elem_hdr->size);
+
+ if (elem_hdr->size < sizeof(*elem_hdr) ||
+ elem_hdr->size > remaining) {
+ dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n",
+ elem_hdr->type, elem_hdr->size);
+ return -EINVAL;
+ }
+
+ /* process structure data */
+ switch (elem_hdr->type) {
+ case SOF_EXT_MAN_ELEM_FW_VERSION:
+ ret = ext_man_get_fw_version(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_WINDOW:
+ ret = ext_man_get_windows(sdev, elem_hdr);
+ break;
+ case SOF_EXT_MAN_ELEM_CC_VERSION:
+ ret = ext_man_get_cc_info(sdev, elem_hdr);
+ break;
+ default:
+ dev_warn(sdev->dev, "warning: unknown sof_ext_man header type %d size 0x%X\n",
+ elem_hdr->type, elem_hdr->size);
+ break;
+ }
+
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n",
+ elem_hdr->type, elem_hdr->size);
+ return ret;
+ }
+
+ remaining -= elem_hdr->size;
+ iptr += elem_hdr->size;
+ }
+
+ if (remaining) {
+ dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
+ return -EINVAL;
+ }
+
+ return ext_man_size;
+}
+
/*
* IPC Firmware ready.
*/
@@ -379,12 +532,19 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
}
EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
-static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
+static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
+ size_t fw_offset)
{
struct snd_sof_fw_header *header;
+ size_t fw_size = fw->size - fw_offset;
+
+ if (fw->size <= fw_offset) {
+ dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
+ return -EINVAL;
+ }
/* Read the header information from the data pointer */
- header = (struct snd_sof_fw_header *)fw->data;
+ header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
/* verify FW sig */
if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
@@ -393,9 +553,9 @@ static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
}
/* check size is valid */
- if (fw->size != header->file_size + sizeof(*header)) {
+ if (fw_size != header->file_size + sizeof(*header)) {
dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
- fw->size, header->file_size + sizeof(*header));
+ fw_size, header->file_size + sizeof(*header));
return -EINVAL;
}
@@ -406,7 +566,8 @@ static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
return 0;
}
-static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw)
+static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
+ size_t fw_offset)
{
struct snd_sof_fw_header *header;
struct snd_sof_mod_hdr *module;
@@ -415,14 +576,15 @@ static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw)
int ret, count;
size_t remaining;
- header = (struct snd_sof_fw_header *)fw->data;
+ header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
load_module = sof_ops(sdev)->load_module;
if (!load_module)
return -EINVAL;
/* parse each module */
- module = (struct snd_sof_mod_hdr *)((u8 *)(fw->data) + sizeof(*header));
- remaining = fw->size - sizeof(*header);
+ module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
+ sizeof(*header));
+ remaining = fw->size - sizeof(*header) - fw_offset;
/* check for wrap */
if (remaining > fw->size) {
dev_err(sdev->dev, "error: fw size smaller than header size\n");
@@ -464,6 +626,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
const char *fw_filename;
+ ssize_t ext_man_size;
int ret;
/* Don't request firmware again if firmware is already requested */
@@ -481,11 +644,27 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
if (ret < 0) {
dev_err(sdev->dev, "error: request firmware %s failed err: %d\n",
fw_filename, ret);
+ goto err;
} else {
dev_dbg(sdev->dev, "request_firmware %s successful\n",
fw_filename);
}
+ /* check for extended manifest */
+ ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
+ if (ext_man_size > 0) {
+ /* when no error occurred, drop extended manifest */
+ plat_data->fw_offset = ext_man_size;
+ } else if (!ext_man_size) {
+ /* No extended manifest, so nothing to skip during FW load */
+ dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
+ } else {
+ ret = ext_man_size;
+ dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
+ fw_filename, ret);
+ }
+
+err:
kfree(fw_filename);
return ret;
@@ -502,7 +681,7 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
return ret;
/* make sure the FW header and file is valid */
- ret = check_header(sdev, plat_data->fw);
+ ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
if (ret < 0) {
dev_err(sdev->dev, "error: invalid FW header\n");
goto error;
@@ -516,7 +695,7 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
}
/* parse and load firmware modules to DSP */
- ret = load_modules(sdev, plat_data->fw);
+ ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
if (ret < 0) {
dev_err(sdev->dev, "error: invalid FW modules\n");
goto error;
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index 2233146386cc..ce053ba8f2e8 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -66,7 +66,6 @@ int sof_nocodec_setup(struct device *dev,
const struct snd_sof_dsp_ops *ops)
{
struct snd_soc_dai_link *links;
- int ret;
/* create dummy BE dai_links */
links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
@@ -74,9 +73,8 @@ int sof_nocodec_setup(struct device *dev,
if (!links)
return -ENOMEM;
- ret = sof_nocodec_bes_setup(dev, ops, links, ops->num_drv,
- &sof_nocodec_card);
- return ret;
+ return sof_nocodec_bes_setup(dev, ops, links, ops->num_drv,
+ &sof_nocodec_card);
}
EXPORT_SYMBOL(sof_nocodec_setup);
diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c
index 7a27c3b719e7..1a394b4c6a2f 100644
--- a/sound/soc/sof/ops.c
+++ b/sound/soc/sof/ops.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index a771500ac442..b21632f5511a 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 47cd741f2a8c..22fe9d5e932b 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -639,6 +639,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
struct snd_sof_dai *dai =
snd_sof_find_dai(component, (char *)rtd->dai_link->name);
+ struct snd_soc_dpcm *dpcm;
/* no topology exists for this BE, try a common configuration */
if (!dai) {
@@ -702,7 +703,16 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
}
break;
case SOF_DAI_INTEL_HDA:
- /* do nothing for HDA dai_link */
+ /*
+ * HDaudio does not follow the default trigger
+ * sequence due to firmware implementation
+ */
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
+ struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+ fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
+ SND_SOC_DPCM_TRIGGER_POST;
+ }
break;
case SOF_DAI_INTEL_ALH:
/* do nothing for ALH dai_link */
@@ -785,11 +795,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->pointer = sof_pcm_pointer;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
- pd->compr_ops = &sof_compressed_ops;
+ pd->compress_ops = &sof_compressed_ops;
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
/* override cops when probe support is enabled */
- pd->compr_ops = &sof_probe_compressed_ops;
+ pd->compress_ops = &sof_probe_compressed_ops;
#endif
pd->pcm_construct = sof_pcm_new;
pd->ignore_machine = drv_name;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index c410822d9920..92e5f9b15f3a 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -90,7 +90,10 @@ static int sof_resume(struct device *dev, bool runtime_resume)
int ret;
/* do nothing if dsp resume callbacks are not set */
- if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume)
+ if (!runtime_resume && !sof_ops(sdev)->resume)
+ return 0;
+
+ if (runtime_resume && !sof_ops(sdev)->runtime_resume)
return 0;
/* DSP was never successfully started, nothing to resume */
@@ -111,8 +114,12 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
- /* Nothing further to do if resuming from a low-power D0 substate */
- if (!runtime_resume && old_state == SOF_DSP_PM_D0)
+ /*
+ * Nothing further to be done for platforms that support the low power
+ * D0 substate.
+ */
+ if (!runtime_resume && sof_ops(sdev)->set_power_state &&
+ old_state == SOF_DSP_PM_D0)
return 0;
sdev->fw_state = SOF_FW_BOOT_PREPARE;
@@ -175,7 +182,10 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
int ret;
/* do nothing if dsp suspend callback is not set */
- if (!sof_ops(sdev)->suspend)
+ if (!runtime_suspend && !sof_ops(sdev)->suspend)
+ return 0;
+
+ if (runtime_suspend && !sof_ops(sdev)->runtime_suspend)
return 0;
if (sdev->fw_state != SOF_FW_BOOT_COMPLETE)
@@ -250,6 +260,15 @@ suspend:
return ret;
}
+int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
+{
+ /* Notify DSP of upcoming power down */
+ if (sof_ops(sdev)->remove)
+ return sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
+
+ return 0;
+}
+
int snd_sof_runtime_suspend(struct device *dev)
{
return sof_suspend(dev, true);
diff --git a/sound/soc/sof/probe.c b/sound/soc/sof/probe.c
index c38169fe00c5..14509f4d3f86 100644
--- a/sound/soc/sof/probe.c
+++ b/sound/soc/sof/probe.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h
index 45daa5552834..b04b728c7224 100644
--- a/sound/soc/sof/probe.h
+++ b/sound/soc/sof/probe.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index 1278aa95effa..c5eaaa978054 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index fc4ed2a8a914..1c7698f8edd6 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 875a5fc13297..9629994fe463 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index 16e49f2ee629..f492c5dfa659 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// Copyright 2019 NXP
//
@@ -14,6 +14,7 @@
extern struct snd_sof_dsp_ops sof_imx8_ops;
extern struct snd_sof_dsp_ops sof_imx8x_ops;
+extern struct snd_sof_dsp_ops sof_imx8m_ops;
/* platform specific devices */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8)
@@ -34,6 +35,16 @@ static struct sof_dev_desc sof_of_imx8qm_desc = {
};
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M)
+static struct sof_dev_desc sof_of_imx8mp_desc = {
+ .default_fw_path = "imx/sof",
+ .default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8m.ri",
+ .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
+ .ops = &sof_imx8m_ops,
+};
+#endif
+
static const struct dev_pm_ops sof_of_pm = {
SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
@@ -114,6 +125,9 @@ static const struct of_device_id sof_of_ids[] = {
{ .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
{ .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M)
+ { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc},
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, sof_of_ids);
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index cec631a1389b..b13697dab7c0 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -435,6 +435,8 @@ static const struct pci_device_id sof_pci_ids[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
{ PCI_DEVICE(0x8086, 0x4b55),
.driver_data = (unsigned long)&ehl_desc},
+ { PCI_DEVICE(0x8086, 0x4b58),
+ .driver_data = (unsigned long)&ehl_desc},
#endif
{ 0, }
};
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index a4b297c842df..64f28e082049 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* 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.
@@ -453,6 +453,7 @@ int snd_sof_runtime_resume(struct device *dev);
int snd_sof_runtime_idle(struct device *dev);
int snd_sof_resume(struct device *dev);
int snd_sof_suspend(struct device *dev);
+int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev);
int snd_sof_prepare(struct device *dev);
void snd_sof_complete(struct device *dev);
@@ -479,7 +480,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset);
*/
struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev);
void snd_sof_ipc_free(struct snd_sof_dev *sdev);
-int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
+void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev);
int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev,
struct sof_ipc_pcm_params *params);
@@ -522,7 +523,7 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev);
/*
* Platform specific ops.
*/
-extern struct snd_compr_ops sof_compressed_ops;
+extern struct snd_compress_ops sof_compressed_ops;
/*
* DSP Architectures.
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index ab2b69de1d4d..6a9703e5ff60 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
@@ -430,6 +430,8 @@ static const struct sof_process_types sof_process[] = {
{"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR},
{"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX},
{"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX},
+ {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK},
+ {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP},
};
static enum sof_ipc_process_type find_process(const char *name)
@@ -655,6 +657,16 @@ static const struct sof_topology_token ssp_tokens[] = {
};
+/* ALH */
+static const struct sof_topology_token alh_tokens[] = {
+ {SOF_TKN_INTEL_ALH_RATE,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_alh_params, rate), 0},
+ {SOF_TKN_INTEL_ALH_CH,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_alh_params, channels), 0},
+};
+
/* DMIC */
static const struct sof_topology_token dmic_tokens[] = {
{SOF_TKN_INTEL_DMIC_DRIVER_VERSION,
@@ -742,6 +754,12 @@ static const struct sof_topology_token dmic_pdm_tokens[] = {
/* HDA */
static const struct sof_topology_token hda_tokens[] = {
+ {SOF_TKN_INTEL_HDA_RATE,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_hda_params, rate), 0},
+ {SOF_TKN_INTEL_HDA_CH,
+ SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_hda_params, channels), 0},
};
/* Leds */
@@ -752,13 +770,15 @@ static const struct sof_topology_token led_tokens[] = {
get_token_u32, offsetof(struct snd_sof_led_control, direction), 0},
};
-static void sof_parse_uuid_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array)
+static int sof_parse_uuid_tokens(struct snd_soc_component *scomp,
+ void *object,
+ const struct sof_topology_token *tokens,
+ int count,
+ struct snd_soc_tplg_vendor_array *array,
+ size_t offset)
{
struct snd_soc_tplg_vendor_uuid_elem *elem;
+ int found = 0;
int i, j;
/* parse element by element */
@@ -776,19 +796,26 @@ static void sof_parse_uuid_tokens(struct snd_soc_component *scomp,
continue;
/* matched - now load token */
- tokens[j].get_token(elem, object, tokens[j].offset,
+ tokens[j].get_token(elem, object,
+ offset + tokens[j].offset,
tokens[j].size);
+
+ found++;
}
}
+
+ return found;
}
-static void sof_parse_string_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array)
+static int sof_parse_string_tokens(struct snd_soc_component *scomp,
+ void *object,
+ const struct sof_topology_token *tokens,
+ int count,
+ struct snd_soc_tplg_vendor_array *array,
+ size_t offset)
{
struct snd_soc_tplg_vendor_string_elem *elem;
+ int found = 0;
int i, j;
/* parse element by element */
@@ -806,24 +833,27 @@ static void sof_parse_string_tokens(struct snd_soc_component *scomp,
continue;
/* matched - now load token */
- tokens[j].get_token(elem, object, tokens[j].offset,
+ tokens[j].get_token(elem, object,
+ offset + tokens[j].offset,
tokens[j].size);
+
+ found++;
}
}
+
+ return found;
}
-static void sof_parse_word_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array)
+static int sof_parse_word_tokens(struct snd_soc_component *scomp,
+ void *object,
+ const struct sof_topology_token *tokens,
+ int count,
+ struct snd_soc_tplg_vendor_array *array,
+ size_t offset)
{
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_vendor_value_elem *elem;
- size_t size = sizeof(struct sof_ipc_dai_dmic_pdm_ctrl);
+ int found = 0;
int i, j;
- u32 offset;
- u32 *index = NULL;
/* parse element by element */
for (i = 0; i < le32_to_cpu(array->num_elems); i++) {
@@ -842,58 +872,45 @@ static void sof_parse_word_tokens(struct snd_soc_component *scomp,
if (tokens[j].token != le32_to_cpu(elem->token))
continue;
- /* pdm config array index */
- if (sdev->private)
- index = sdev->private;
-
- /* matched - determine offset */
- switch (tokens[j].token) {
- case SOF_TKN_INTEL_DMIC_PDM_CTRL_ID:
-
- /* inc number of pdm array index */
- if (index)
- (*index)++;
- /* fallthrough */
- case SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable:
- case SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable:
- case SOF_TKN_INTEL_DMIC_PDM_POLARITY_A:
- case SOF_TKN_INTEL_DMIC_PDM_POLARITY_B:
- case SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE:
- case SOF_TKN_INTEL_DMIC_PDM_SKEW:
-
- /* check if array index is valid */
- if (!index || *index == 0) {
- dev_err(scomp->dev,
- "error: invalid array offset\n");
- continue;
- } else {
- /* offset within the pdm config array */
- offset = size * (*index - 1);
- }
- break;
- default:
- offset = 0;
- break;
- }
-
/* load token */
tokens[j].get_token(elem, object,
offset + tokens[j].offset,
tokens[j].size);
+
+ found++;
}
}
+
+ return found;
}
-static int sof_parse_tokens(struct snd_soc_component *scomp,
- void *object,
- const struct sof_topology_token *tokens,
- int count,
- struct snd_soc_tplg_vendor_array *array,
- int priv_size)
-{
+/**
+ * sof_parse_token_sets - Parse multiple sets of tokens
+ * @scomp: pointer to soc component
+ * @object: target ipc struct for parsed values
+ * @tokens: token definition array describing what tokens to parse
+ * @count: number of tokens in definition array
+ * @array: source pointer to consecutive vendor arrays to be parsed
+ * @priv_size: total size of the consecutive source arrays
+ * @sets: number of similar token sets to be parsed, 1 set has count elements
+ * @object_size: offset to next target ipc struct with multiple sets
+ *
+ * This function parses multiple sets of tokens in vendor arrays into
+ * consecutive ipc structs.
+ */
+static int sof_parse_token_sets(struct snd_soc_component *scomp,
+ void *object,
+ const struct sof_topology_token *tokens,
+ int count,
+ struct snd_soc_tplg_vendor_array *array,
+ int priv_size, int sets, size_t object_size)
+{
+ size_t offset = 0;
+ int found = 0;
+ int total = 0;
int asize;
- while (priv_size > 0) {
+ while (priv_size > 0 && total < count * sets) {
asize = le32_to_cpu(array->size);
/* validate asize */
@@ -914,19 +931,19 @@ static int sof_parse_tokens(struct snd_soc_component *scomp,
/* call correct parser depending on type */
switch (le32_to_cpu(array->type)) {
case SND_SOC_TPLG_TUPLE_TYPE_UUID:
- sof_parse_uuid_tokens(scomp, object, tokens, count,
- array);
+ found += sof_parse_uuid_tokens(scomp, object, tokens,
+ count, array, offset);
break;
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
- sof_parse_string_tokens(scomp, object, tokens, count,
- array);
+ found += sof_parse_string_tokens(scomp, object, tokens,
+ count, array, offset);
break;
case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
case SND_SOC_TPLG_TUPLE_TYPE_WORD:
case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
- sof_parse_word_tokens(scomp, object, tokens, count,
- array);
+ found += sof_parse_word_tokens(scomp, object, tokens,
+ count, array, offset);
break;
default:
dev_err(scomp->dev, "error: unknown token type %d\n",
@@ -937,10 +954,35 @@ static int sof_parse_tokens(struct snd_soc_component *scomp,
/* next array */
array = (struct snd_soc_tplg_vendor_array *)((u8 *)array
+ asize);
+
+ /* move to next target struct */
+ if (found >= count) {
+ offset += object_size;
+ total += found;
+ found = 0;
+ }
}
+
return 0;
}
+static int sof_parse_tokens(struct snd_soc_component *scomp,
+ void *object,
+ const struct sof_topology_token *tokens,
+ int count,
+ struct snd_soc_tplg_vendor_array *array,
+ int priv_size)
+{
+ /*
+ * sof_parse_tokens is used when topology contains only a single set of
+ * identical tuples arrays. So additional parameters to
+ * sof_parse_token_sets are sets = 1 (only 1 set) and
+ * object_size = 0 (irrelevant).
+ */
+ return sof_parse_token_sets(scomp, object, tokens, count, array,
+ priv_size, 1, 0);
+}
+
static void sof_dbg_comp_config(struct snd_soc_component *scomp,
struct sof_ipc_comp_config *config)
{
@@ -1259,15 +1301,45 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
switch (w->id) {
case snd_soc_dapm_dai_out:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai)
- cpu_dai->capture_widget = w;
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ /*
+ * Please create DAI widget in the right order
+ * to ensure BE will connect to the right DAI
+ * widget.
+ */
+ if (!cpu_dai->capture_widget) {
+ cpu_dai->capture_widget = w;
+ break;
+ }
+ }
+ if (i == rtd->num_cpus) {
+ dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
+ w->name);
+
+ return -EINVAL;
+ }
dai->name = rtd->dai_link->name;
dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
w->name, rtd->dai_link->name);
break;
case snd_soc_dapm_dai_in:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai)
- cpu_dai->playback_widget = w;
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ /*
+ * Please create DAI widget in the right order
+ * to ensure BE will connect to the right DAI
+ * widget.
+ */
+ if (!cpu_dai->playback_widget) {
+ cpu_dai->playback_widget = w;
+ break;
+ }
+ }
+ if (i == rtd->num_cpus) {
+ dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
+ w->name);
+
+ return -EINVAL;
+ }
dai->name = rtd->dai_link->name;
dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
w->name, rtd->dai_link->name);
@@ -2604,7 +2676,11 @@ static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config,
}
}
-/* set config for all DAI's with name matching the link name */
+/*
+ * Send IPC and set the same config for all DAIs with name matching the link
+ * name. Note that the function can only be used for the case that all DAIs
+ * have a common DAI config for now.
+ */
static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
struct snd_soc_dai_link *link,
struct sof_ipc_dai_config *config)
@@ -2617,6 +2693,27 @@ static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size,
continue;
if (strcmp(link->name, dai->name) == 0) {
+ struct sof_ipc_reply reply;
+ int ret;
+
+ /*
+ * the same dai config will be applied to all DAIs in
+ * the same dai link. We have to ensure that the ipc
+ * dai config's dai_index match to the component's
+ * dai_index.
+ */
+ config->dai_index = dai->comp_dai.dai_index;
+
+ /* send message to DSP */
+ ret = sof_ipc_tx_message(sdev->ipc,
+ config->hdr.cmd, config, size,
+ &reply, sizeof(reply));
+
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to set DAI config for %s index %d\n",
+ dai->name, config->dai_index);
+ return ret;
+ }
dai->dai_config = kmemdup(config, size, GFP_KERNEL);
if (!dai->dai_config)
return -ENOMEM;
@@ -2649,7 +2746,6 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_reply reply;
u32 size = sizeof(*config);
int ret;
@@ -2698,17 +2794,6 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size, &reply,
- sizeof(reply));
-
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to set DAI config for SSP%d\n",
- config->dai_index);
- return ret;
- }
-
/* set config for all DAI's with name matching the link name */
ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
@@ -2726,7 +2811,6 @@ static int sof_link_sai_load(struct snd_soc_component *scomp, int index,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_reply reply;
u32 size = sizeof(*config);
int ret;
@@ -2766,17 +2850,6 @@ static int sof_link_sai_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size, &reply,
- sizeof(reply));
-
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to set DAI config for SAI%d\n",
- config->dai_index);
- return ret;
- }
-
/* set config for all DAI's with name matching the link name */
ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
@@ -2794,7 +2867,6 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_reply reply;
u32 size = sizeof(*config);
int ret;
@@ -2835,16 +2907,6 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size, &reply,
- sizeof(reply));
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to set DAI config for ESAI%d\n",
- config->dai_index);
- return ret;
- }
-
/* set config for all DAI's with name matching the link name */
ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
@@ -2862,18 +2924,12 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_soc_tplg_private *private = &cfg->priv;
- struct sof_ipc_dai_config *ipc_config;
- struct sof_ipc_reply reply;
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
- u32 size;
+ size_t size = sizeof(*config);
int ret, j;
- /*
- * config is only used for the common params in dmic_params structure
- * that does not include the PDM controller config array
- * Set the common params to 0.
- */
+ /* Ensure the entire DMIC config struct is zeros */
memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params));
/* get DMIC tokens */
@@ -2887,34 +2943,20 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
}
/*
- * allocate memory for dmic dai config accounting for the
- * variable number of active pdm controllers
- * This will be the ipc payload for setting dai config
- */
- size = sizeof(*config) + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl) *
- config->dmic.num_pdm_active;
-
- ipc_config = kzalloc(size, GFP_KERNEL);
- if (!ipc_config)
- return -ENOMEM;
-
- /* copy the common dai config and dmic params */
- memcpy(ipc_config, config, sizeof(*config));
-
- /*
* alloc memory for private member
* Used to track the pdm config array index currently being parsed
*/
sdev->private = kzalloc(sizeof(u32), GFP_KERNEL);
- if (!sdev->private) {
- kfree(ipc_config);
+ if (!sdev->private)
return -ENOMEM;
- }
/* get DMIC PDM tokens */
- ret = sof_parse_tokens(scomp, &ipc_config->dmic.pdm[0], dmic_pdm_tokens,
+ ret = sof_parse_token_sets(scomp, &config->dmic.pdm[0], dmic_pdm_tokens,
ARRAY_SIZE(dmic_pdm_tokens), private->array,
- le32_to_cpu(private->size));
+ le32_to_cpu(private->size),
+ config->dmic.num_pdm_active,
+ sizeof(struct sof_ipc_dai_dmic_pdm_ctrl));
+
if (ret != 0) {
dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n",
le32_to_cpu(private->size));
@@ -2922,125 +2964,53 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
}
/* set IPC header size */
- ipc_config->hdr.size = size;
+ config->hdr.size = size;
/* debug messages */
dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n",
- ipc_config->dai_index, ipc_config->dmic.driver_ipc_version);
+ config->dai_index, config->dmic.driver_ipc_version);
dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n",
- ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max,
- ipc_config->dmic.duty_min);
+ config->dmic.pdmclk_min, config->dmic.pdmclk_max,
+ config->dmic.duty_min);
dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n",
- ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs,
- ipc_config->dmic.num_pdm_active);
- dev_dbg(scomp->dev, "fifo word length %hd\n",
- ipc_config->dmic.fifo_bits);
+ config->dmic.duty_max, config->dmic.fifo_fs,
+ config->dmic.num_pdm_active);
+ dev_dbg(scomp->dev, "fifo word length %hd\n", config->dmic.fifo_bits);
- for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) {
+ for (j = 0; j < config->dmic.num_pdm_active; j++) {
dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n",
- ipc_config->dmic.pdm[j].id,
- ipc_config->dmic.pdm[j].enable_mic_a,
- ipc_config->dmic.pdm[j].enable_mic_b);
+ config->dmic.pdm[j].id,
+ config->dmic.pdm[j].enable_mic_a,
+ config->dmic.pdm[j].enable_mic_b);
dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n",
- ipc_config->dmic.pdm[j].id,
- ipc_config->dmic.pdm[j].polarity_mic_a,
- ipc_config->dmic.pdm[j].polarity_mic_b);
+ config->dmic.pdm[j].id,
+ config->dmic.pdm[j].polarity_mic_a,
+ config->dmic.pdm[j].polarity_mic_b);
dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n",
- ipc_config->dmic.pdm[j].id,
- ipc_config->dmic.pdm[j].clk_edge,
- ipc_config->dmic.pdm[j].skew);
+ config->dmic.pdm[j].id,
+ config->dmic.pdm[j].clk_edge,
+ config->dmic.pdm[j].skew);
}
- if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) {
- /* this takes care of backwards compatible handling of fifo_bits_b */
- ipc_config->dmic.reserved_2 = ipc_config->dmic.fifo_bits;
- }
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- ipc_config->hdr.cmd, ipc_config, size, &reply,
- sizeof(reply));
-
- if (ret < 0) {
- dev_err(scomp->dev,
- "error: failed to set DAI config for DMIC%d\n",
- config->dai_index);
- goto err;
- }
+ /*
+ * this takes care of backwards compatible handling of fifo_bits_b.
+ * It is deprecated since firmware ABI version 3.0.1.
+ */
+ if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1))
+ config->dmic.fifo_bits_b = config->dmic.fifo_bits;
/* set config for all DAI's with name matching the link name */
- ret = sof_set_dai_config(sdev, size, link, ipc_config);
+ ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n",
config->dai_index);
err:
kfree(sdev->private);
- kfree(ipc_config);
return ret;
}
-/*
- * for hda link, playback and capture are supported by different dai
- * in FW. Here get the dai_index, set dma channel of each dai
- * and send config to FW. In FW, each dai sets config by dai_index
- */
-static int sof_link_hda_process(struct snd_sof_dev *sdev,
- struct snd_soc_dai_link *link,
- struct sof_ipc_dai_config *config)
-{
- struct sof_ipc_reply reply;
- u32 size = sizeof(*config);
- struct snd_sof_dai *sof_dai;
- int found = 0;
- int ret;
-
- list_for_each_entry(sof_dai, &sdev->dai_list, list) {
- if (!sof_dai->name)
- continue;
-
- if (strcmp(link->name, sof_dai->name) == 0) {
- config->dai_index = sof_dai->comp_dai.dai_index;
- found = 1;
-
- config->hda.link_dma_ch = DMA_CHAN_INVALID;
-
- /* save config in dai component */
- sof_dai->dai_config = kmemdup(config, size, GFP_KERNEL);
- if (!sof_dai->dai_config)
- return -ENOMEM;
-
- sof_dai->cpu_dai_name = link->cpus->dai_name;
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size,
- &reply, sizeof(reply));
-
- if (ret < 0) {
- dev_err(sdev->dev, "error: failed to set DAI config for direction:%d of HDA dai %d\n",
- sof_dai->comp_dai.direction,
- config->dai_index);
-
- return ret;
- }
- }
- }
-
- /*
- * machine driver may define a dai link with playback and capture
- * dai enabled, but the dai link in topology would support both, one
- * or none of them. Here print a warning message to notify user
- */
- if (!found) {
- dev_warn(sdev->dev, "warning: failed to find dai for dai link %s",
- link->name);
- }
-
- return 0;
-}
-
static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
struct snd_soc_dai_link *link,
struct snd_soc_tplg_link_config *cfg,
@@ -3058,7 +3028,7 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
config->hdr.size = size;
/* get any bespoke DAI tokens */
- ret = sof_parse_tokens(scomp, config, hda_tokens,
+ ret = sof_parse_tokens(scomp, &config->hda, hda_tokens,
ARRAY_SIZE(hda_tokens), private->array,
le32_to_cpu(private->size));
if (ret != 0) {
@@ -3067,6 +3037,9 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
return ret;
}
+ dev_dbg(scomp->dev, "HDA config rate %d channels %d\n",
+ config->hda.rate, config->hda.channels);
+
dai = snd_soc_find_dai(link->cpus);
if (!dai) {
dev_err(scomp->dev, "error: failed to find dai %s in %s",
@@ -3074,7 +3047,9 @@ static int sof_link_hda_load(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- ret = sof_link_hda_process(sdev, link, config);
+ config->hda.link_dma_ch = DMA_CHAN_INVALID;
+
+ ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
dev_err(scomp->dev, "error: failed to process hda dai link %s",
link->name);
@@ -3089,24 +3064,22 @@ static int sof_link_alh_load(struct snd_soc_component *scomp, int index,
struct sof_ipc_dai_config *config)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct sof_ipc_reply reply;
+ struct snd_soc_tplg_private *private = &cfg->priv;
u32 size = sizeof(*config);
int ret;
- /* init IPC */
- config->hdr.size = size;
-
- /* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- config->hdr.cmd, config, size, &reply,
- sizeof(reply));
-
- if (ret < 0) {
- dev_err(scomp->dev, "error: failed to set DAI config for ALH %d\n",
- config->dai_index);
+ ret = sof_parse_tokens(scomp, &config->alh, alh_tokens,
+ ARRAY_SIZE(alh_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(scomp->dev, "error: parse alh tokens failed %d\n",
+ le32_to_cpu(private->size));
return ret;
}
+ /* init IPC */
+ config->hdr.size = size;
+
/* set config for all DAI's with name matching the link name */
ret = sof_set_dai_config(sdev, size, link, config);
if (ret < 0)
@@ -3141,9 +3114,17 @@ static int sof_link_load(struct snd_soc_component *scomp, int index,
if (!link->no_pcm) {
link->nonatomic = true;
- /* set trigger order */
- link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST;
- link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST;
+ /*
+ * set default trigger order for all links. Exceptions to
+ * the rule will be handled in sof_pcm_dai_link_fixup()
+ * For playback, the sequence is the following: start FE,
+ * start BE, stop BE, stop FE; for Capture the sequence is
+ * inverted start BE, start FE, stop FE, stop BE
+ */
+ link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
+ SND_SOC_DPCM_TRIGGER_PRE;
+ link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
+ SND_SOC_DPCM_TRIGGER_POST;
/* nothing more to do for FE dai links */
return 0;
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index d815090252f8..69889241a092 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c
index 9831eb57df6c..5539d3afbe8f 100644
--- a/sound/soc/sof/utils.c
+++ b/sound/soc/sof/utils.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.
diff --git a/sound/soc/sof/xtensa/Makefile b/sound/soc/sof/xtensa/Makefile
index cc89c7472a38..b8376ea04bcf 100644
--- a/sound/soc/sof/xtensa/Makefile
+++ b/sound/soc/sof/xtensa/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-xtensa-dsp-objs := core.o
diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c
index ea08651f0bb3..bbb9a2282ed9 100644
--- a/sound/soc/sof/xtensa/core.c
+++ b/sound/soc/sof/xtensa/core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// 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.