summaryrefslogtreecommitdiff
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/core.c174
-rw-r--r--sound/soc/sof/imx/Kconfig19
-rw-r--r--sound/soc/sof/imx/Makefile2
-rw-r--r--sound/soc/sof/imx/imx8.c219
-rw-r--r--sound/soc/sof/imx/imx8m.c523
-rw-r--r--sound/soc/sof/intel/apl.c12
-rw-r--r--sound/soc/sof/intel/bdw.c19
-rw-r--r--sound/soc/sof/intel/byt.c41
-rw-r--r--sound/soc/sof/intel/cnl.c12
-rw-r--r--sound/soc/sof/loader.c44
-rw-r--r--sound/soc/sof/nocodec.c12
-rw-r--r--sound/soc/sof/ops.h26
-rw-r--r--sound/soc/sof/pcm.c37
-rw-r--r--sound/soc/sof/pm.c3
-rw-r--r--sound/soc/sof/sof-acpi-dev.c11
-rw-r--r--sound/soc/sof/sof-of-dev.c82
-rw-r--r--sound/soc/sof/sof-pci-dev.c22
-rw-r--r--sound/soc/sof/sof-priv.h19
-rw-r--r--sound/soc/sof/topology.c153
-rw-r--r--sound/soc/sof/trace.c4
-rw-r--r--sound/soc/sof/utils.c60
21 files changed, 1298 insertions, 196 deletions
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 2a6b84d2781e..fa344968986a 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -10,7 +10,6 @@
#include <linux/firmware.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
#include <sound/soc.h>
#include <sound/sof.h>
#include "sof-priv.h"
@@ -196,67 +195,9 @@ out:
EXPORT_SYMBOL(snd_sof_get_status);
/*
- * Generic buffer page table creation.
- * Take the each physical page address and drop the least significant unused
- * bits from each (based on PAGE_SIZE). Then pack valid page address bits
- * into compressed page table.
- */
-
-int snd_sof_create_page_table(struct snd_sof_dev *sdev,
- struct snd_dma_buffer *dmab,
- unsigned char *page_table, size_t size)
-{
- int i, pages;
-
- pages = snd_sgbuf_aligned_pages(size);
-
- dev_dbg(sdev->dev, "generating page table for %p size 0x%zx pages %d\n",
- dmab->area, size, pages);
-
- for (i = 0; i < pages; i++) {
- /*
- * The number of valid address bits for each page is 20.
- * idx determines the byte position within page_table
- * where the current page's address is stored
- * in the compressed page_table.
- * This can be calculated by multiplying the page number by 2.5.
- */
- u32 idx = (5 * i) >> 1;
- u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
- u8 *pg_table;
-
- dev_vdbg(sdev->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
-
- pg_table = (u8 *)(page_table + idx);
-
- /*
- * pagetable compression:
- * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
- * ___________pfn 0__________ __________pfn 1___________ _pfn 2...
- * .... .... .... .... .... .... .... .... .... .... ....
- * It is created by:
- * 1. set current location to 0, PFN index i to 0
- * 2. put pfn[i] at current location in Little Endian byte order
- * 3. calculate an intermediate value as
- * x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
- * 4. put x at offset (current location + 2) in LE byte order
- * 5. increment current location by 5 bytes, increment i by 2
- * 6. continue to (2)
- */
- if (i & 1)
- put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
- pg_table);
- else
- put_unaligned_le32(pfn, pg_table);
- }
-
- return pages;
-}
-
-/*
* SOF Driver enumeration.
*/
-static int sof_machine_check(struct snd_sof_dev *sdev)
+int sof_machine_check(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)
@@ -277,8 +218,10 @@ static int sof_machine_check(struct snd_sof_dev *sdev)
if (!machine)
return -ENOMEM;
- ret = sof_nocodec_setup(sdev->dev, plat_data, machine,
- plat_data->desc, plat_data->desc->ops);
+ machine->drv_name = "sof-nocodec";
+ plat_data->fw_filename = plat_data->desc->nocodec_fw_filename;
+ plat_data->tplg_filename = plat_data->desc->nocodec_tplg_filename;
+ ret = sof_nocodec_setup(sdev->dev, plat_data->desc->ops);
if (ret < 0)
return ret;
@@ -287,53 +230,45 @@ static int sof_machine_check(struct snd_sof_dev *sdev)
return 0;
#endif
}
+EXPORT_SYMBOL(sof_machine_check);
-/*
- * FW Boot State Transition Diagram
- *
- * +-----------------------------------------------------------------------+
- * | |
- * ------------------ ------------------ |
- * | | | | |
- * | BOOT_FAILED | | READY_FAILED |-------------------------+ |
- * | | | | | |
- * ------------------ ------------------ | |
- * ^ ^ | |
- * | | | |
- * (FW Boot Timeout) (FW_READY FAIL) | |
- * | | | |
- * | | | |
- * ------------------ | ------------------ | |
- * | | | | | | |
- * | IN_PROGRESS |---------------+------------->| COMPLETE | | |
- * | | (FW Boot OK) (FW_READY OK) | | | |
- * ------------------ ------------------ | |
- * ^ | | |
- * | | | |
- * (FW Loading OK) (System Suspend/Runtime Suspend)
- * | | | |
- * | | | |
- * ------------------ ------------------ | | |
- * | | | |<-----+ | |
- * | PREPARE | | NOT_STARTED |<---------------------+ |
- * | | | |<---------------------------+
- * ------------------ ------------------
- * | ^ | ^
- * | | | |
- * | +-----------------------+ |
- * | (DSP Probe OK) |
- * | |
- * | |
- * +------------------------------------+
- * (System Suspend/Runtime Suspend)
- */
-
-static int sof_probe_continue(struct snd_sof_dev *sdev)
+int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
{
- struct snd_sof_pdata *plat_data = sdev->pdata;
+ struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata;
const char *drv_name;
const void *mach;
int size;
+
+ drv_name = plat_data->machine->drv_name;
+ mach = (const void *)plat_data->machine;
+ size = sizeof(*plat_data->machine);
+
+ /* register machine driver, pass machine info as pdata */
+ plat_data->pdev_mach =
+ platform_device_register_data(sdev->dev, drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ if (IS_ERR(plat_data->pdev_mach))
+ return PTR_ERR(plat_data->pdev_mach);
+
+ dev_dbg(sdev->dev, "created machine %s\n",
+ dev_name(&plat_data->pdev_mach->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL(sof_machine_register);
+
+void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
+{
+ struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata;
+
+ if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
+ platform_device_unregister(plat_data->pdev_mach);
+}
+EXPORT_SYMBOL(sof_machine_unregister);
+
+static int sof_probe_continue(struct snd_sof_dev *sdev)
+{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
int ret;
/* probe the DSP hardware */
@@ -346,7 +281,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
sdev->fw_state = SOF_FW_BOOT_PREPARE;
/* check machine info */
- ret = sof_machine_check(sdev);
+ ret = snd_sof_machine_check(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to get machine info %d\n",
ret);
@@ -419,22 +354,17 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
goto fw_trace_err;
}
- drv_name = plat_data->machine->drv_name;
- mach = (const void *)plat_data->machine;
- size = sizeof(*plat_data->machine);
-
- /* register machine driver, pass machine info as pdata */
- plat_data->pdev_mach =
- platform_device_register_data(sdev->dev, drv_name,
- PLATFORM_DEVID_NONE, mach, size);
-
- if (IS_ERR(plat_data->pdev_mach)) {
- ret = PTR_ERR(plat_data->pdev_mach);
- goto fw_trace_err;
- }
+ ret = snd_sof_machine_register(sdev, plat_data);
+ if (ret < 0)
+ goto fw_run_err;
- dev_dbg(sdev->dev, "created machine %s\n",
- dev_name(&plat_data->pdev_mach->dev));
+ /*
+ * Some platforms in SOF, ex: BYT, may not have their platform PM
+ * callbacks set. Increment the usage count so as to
+ * prevent the device from entering runtime suspend.
+ */
+ if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
+ pm_runtime_get_noresume(sdev->dev);
if (plat_data->sof_probe_complete)
plat_data->sof_probe_complete(sdev->dev);
@@ -545,9 +475,7 @@ int snd_sof_device_remove(struct device *dev)
* will remove the component driver and unload the topology
* before freeing the snd_card.
*/
- if (!IS_ERR_OR_NULL(pdata->pdev_mach))
- platform_device_unregister(pdata->pdev_mach);
-
+ snd_sof_machine_unregister(sdev, pdata);
/*
* Unregistering the machine driver results in unloading the topology.
* Some widgets, ex: scheduler, attempt to power down the core they are
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index b4f0426685c4..805e8b43088e 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -12,7 +12,7 @@ config SND_SOC_SOF_IMX_TOPLEVEL
if SND_SOC_SOF_IMX_TOPLEVEL
config SND_SOC_SOF_IMX8_SUPPORT
- bool "SOF support for i.MX8"
+ tristate "SOF support for i.MX8"
depends on IMX_SCU
select IMX_DSP
help
@@ -21,7 +21,22 @@ config SND_SOC_SOF_IMX8_SUPPORT
If unsure select "N".
config SND_SOC_SOF_IMX8
+ tristate
+
+config SND_SOC_SOF_IMX8M_SUPPORT
+ tristate "SOF support for i.MX8M"
+ depends on IMX_DSP
+ 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
+
+config SND_SOC_SOF_IMX
def_tristate SND_SOC_SOF_OF
- depends on SND_SOC_SOF_IMX8_SUPPORT
+ select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT
+ select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT
endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile
index 6ef908e8c807..d9d8dc1765b8 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)
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 69785f688ddf..8c415884a2df 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -6,6 +6,7 @@
//
// Hardware interface for audio DSP on i.MX8
+#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
@@ -39,9 +40,19 @@
#define MBOX_OFFSET 0x800000
#define MBOX_SIZE 0x1000
+#define IMX8_DSP_CLK_NUM 9
+static const char *imx8_dsp_clks_names[IMX8_DSP_CLK_NUM] =
+{
+ /* ESAI0 clocks */
+ "esai0_core", "esai0_extal", "esai0_fsys", "esai0_spba",
+ /* SAI1 clocks */
+ "sai1_bus", "sai1_mclk0", "sai1_mclk1", "sai1_mclk2", "sai1_mclk3",
+};
+
struct imx8_priv {
struct device *dev;
struct snd_sof_dev *sdev;
+ bool suspended;
/* DSP IPC handler */
struct imx_dsp_ipc *dsp_ipc;
@@ -55,8 +66,54 @@ struct imx8_priv {
struct device **pd_dev;
struct device_link **link;
+ struct clk *dsp_clks[IMX8_DSP_CLK_NUM];
};
+static int imx8_init_clocks(struct snd_sof_dev *sdev)
+{
+ int i;
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+
+ for (i = 0; i < IMX8_DSP_CLK_NUM; i++) {
+ priv->dsp_clks[i] = devm_clk_get(priv->dev, imx8_dsp_clks_names[i]);
+ if (IS_ERR(priv->dsp_clks[i]))
+ priv->dsp_clks[i] = NULL;
+ }
+
+ return 0;
+}
+
+static int imx8_prepare_clocks(struct snd_sof_dev *sdev)
+{
+ int i, ret;
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+
+ for (i = 0; i < IMX8_DSP_CLK_NUM; i++) {
+ ret = clk_prepare_enable(priv->dsp_clks[i]);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to enable clk %s\n",
+ imx8_dsp_clks_names[i]);
+ goto err_dsp_clks;
+ }
+ }
+ return 0;
+
+err_dsp_clks:
+ while (--i >= 0)
+ clk_disable_unprepare(priv->dsp_clks[i]);
+
+ return ret;
+}
+
+static void imx8_disable_clocks(struct snd_sof_dev *sdev)
+{
+ int i;
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+
+ for (i = 0; i < IMX8_DSP_CLK_NUM; i++)
+ clk_disable_unprepare(priv->dsp_clks[i]);
+}
+
static void imx8_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
@@ -119,7 +176,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,
};
@@ -138,7 +195,7 @@ static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
/*
* DSP control.
*/
-static int imx8_run(struct snd_sof_dev *sdev)
+static int imx8x_run(struct snd_sof_dev *sdev)
{
struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private;
int ret;
@@ -178,6 +235,24 @@ static int imx8_run(struct snd_sof_dev *sdev)
return 0;
}
+static int imx8_run(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private;
+ int ret;
+
+ ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ IMX_SC_C_OFS_SEL, 0);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Error system address offset source select\n");
+ return ret;
+ }
+
+ imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
+ RESET_VECTOR_VADDR);
+
+ return 0;
+}
+
static int imx8_probe(struct snd_sof_dev *sdev)
{
struct platform_device *pdev =
@@ -189,7 +264,7 @@ static int imx8_probe(struct snd_sof_dev *sdev)
struct resource res;
u32 base, size;
int ret = 0;
- int i;
+ int i = 0;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -207,6 +282,10 @@ static int imx8_probe(struct snd_sof_dev *sdev)
return priv->num_domains;
}
+ /* power domain already enabled by PM core */
+ if (priv->num_domains == 1)
+ goto done_pm;
+
priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
sizeof(*priv->pd_dev), GFP_KERNEL);
if (!priv->pd_dev)
@@ -234,6 +313,7 @@ static int imx8_probe(struct snd_sof_dev *sdev)
}
}
+done_pm:
ret = imx_scu_get_handle(&priv->sc_ipc);
if (ret) {
dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
@@ -307,6 +387,9 @@ static int imx8_probe(struct snd_sof_dev *sdev)
/* set default mailbox offset for FW ready message */
sdev->dsp_box.offset = MBOX_OFFSET;
+ imx8_init_clocks(sdev);
+ imx8_prepare_clocks(sdev);
+
return 0;
exit_pdev_unregister:
@@ -355,13 +438,78 @@ static int imx8_ipc_pcm_params(struct snd_sof_dev *sdev,
return 0;
}
+int imx8_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+ int i;
+
+ imx8_prepare_clocks(sdev);
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(priv->dsp_ipc, i);
+
+ return 0;
+}
+
+int imx8_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+ int i;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(priv->dsp_ipc, i);
+
+ imx8_disable_clocks(sdev);
+
+ return 0;
+}
+
+int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ return imx8_resume(sdev);
+}
+
+int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev)
+{
+
+ return imx8_suspend(sdev);
+}
+
+int imx8_dsp_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+
+ if (priv->suspended) {
+ imx8_resume(sdev);
+ priv->suspended = false;
+ }
+
+ return 0;
+}
+
+int imx8_dsp_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx8_priv *priv = (struct imx8_priv *)sdev->private;
+
+ if (!priv->suspended) {
+ imx8_suspend(sdev);
+ priv->suspended = true;
+ }
+
+ return 0;
+}
+
+
static struct snd_soc_dai_driver imx8_dai[] = {
{
- .name = "esai-port",
+ .name = "esai0",
+},
+{
+ .name = "sai1",
},
};
-/* i.MX8 ops */
+/* i.MX8 ops */
struct snd_sof_dsp_ops sof_imx8_ops = {
/* probe and remove */
.probe = imx8_probe,
@@ -390,8 +538,67 @@ struct snd_sof_dsp_ops sof_imx8_ops = {
/* DAI drivers */
.drv = imx8_dai,
- .num_drv = 1, /* we have only 1 ESAI interface on i.MX8 */
+ .num_drv = 2, /* we use ESAI0 / SAI1 i.MX8 */
+
+ /* PM */
+ .suspend = imx8_dsp_suspend,
+ .resume = imx8_dsp_resume,
+ .runtime_suspend = imx8_dsp_runtime_suspend,
+ .runtime_resume = imx8_dsp_runtime_resume,
+
+ /* ALSA HW info flags */
+ .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_imx8_ops);
+/* i.MX8X ops */
+struct snd_sof_dsp_ops sof_imx8x_ops = {
+ /* probe and remove */
+ .probe = imx8_probe,
+ .remove = imx8_remove,
+ /* DSP core boot */
+ .run = imx8x_run,
+
+ /* Block IO */
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ /* ipc */
+ .send_msg = imx8_send_msg,
+ .fw_ready = sof_fw_ready,
+ .get_mailbox_offset = imx8_get_mailbox_offset,
+ .get_window_offset = imx8_get_window_offset,
+
+ .ipc_msg_data = imx8_ipc_msg_data,
+ .ipc_pcm_params = imx8_ipc_pcm_params,
+
+ /* module loading */
+ .load_module = snd_sof_parse_module_memcpy,
+ .get_bar_index = imx8_get_bar_index,
+ /* firmware loading */
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ /* DAI drivers */
+ .drv = imx8_dai,
+ .num_drv = 2, /* we use ESAI0 / SAI1 on i.MX8X*/
+
+ /* PM */
+ .suspend = imx8_dsp_suspend,
+ .resume = imx8_dsp_resume,
+ .runtime_suspend = imx8_dsp_runtime_suspend,
+ .runtime_resume = imx8_dsp_runtime_resume,
+
+ /* ALSA HW info flags */
+ .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_imx8x_ops);
+
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
new file mode 100644
index 000000000000..09e136154efc
--- /dev/null
+++ b/sound/soc/sof/imx/imx8m.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+//
+// Copyright 2019 NXP
+//
+// Author: Daniel Baluta <daniel.baluta@nxp.com>
+//
+// Hardware interface for audio DSP on i.MX8
+
+#include <linux/firmware.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pm_domain.h>
+
+#include <linux/module.h>
+#include <sound/sof.h>
+#include <sound/sof/xtensa.h>
+#include <linux/firmware/imx/dsp.h>
+#include <linux/clk.h>
+#include <linux/bits.h>
+
+#include "../ops.h"
+#include "../../fsl/fsl_dsp_audiomix.h"
+
+/* DSP memories */
+#define IRAM_OFFSET 0x10000
+#define IRAM_SIZE (2 * 1024)
+#define DRAM0_OFFSET 0x0
+#define DRAM0_SIZE (32 * 1024)
+#define DRAM1_OFFSET 0x8000
+#define DRAM1_SIZE (32 * 1024)
+#define SYSRAM_OFFSET 0x18000
+#define SYSRAM_SIZE (256 * 1024)
+#define SYSROM_OFFSET 0x58000
+#define SYSROM_SIZE (192 * 1024)
+
+#define RESET_VECTOR_VADDR 0x596f8000
+
+#define MBOX_OFFSET 0x800000
+#define MBOX_SIZE 0x1000
+
+#define IMX8M_DAP_DEBUG 0x28800000
+#define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
+#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
+#define IMX8M_PWRCTL_CORERESET BIT(16)
+
+#define IMX8M_DSP_CLK_NUM 9
+static const char *imx8m_dsp_clks[IMX8M_DSP_CLK_NUM] = {
+ "ocram",
+ "core",
+ "debug",
+ "sdma3",
+ "sai3_bus",
+ "sai3_mclk0",
+ "sai3_mclk1",
+ "sai3_mclk2",
+ "sai3_mclk3",
+};
+
+struct imx8m_priv {
+ struct device *dev;
+ struct snd_sof_dev *sdev;
+ bool suspended;
+
+ struct imx_audiomix_dsp_data *audiomix;
+
+ /* DSP IPC handler */
+ struct imx_dsp_ipc *dsp_ipc;
+ struct platform_device *ipc_dev;
+
+ /* Power domain handling */
+ int num_domains;
+ struct device **pd_dev;
+ struct device_link **link;
+
+ struct clk *clks[IMX8M_DSP_CLK_NUM];
+ void __iomem *dap;
+};
+
+int imx8m_dsp_configure_audmix(struct imx8m_priv *dsp_priv)
+{
+ struct device_node *np;
+ struct platform_device *pdev;
+
+ np = of_find_node_by_name(NULL, "audiomix_dsp");
+ if (!np)
+ return -EPROBE_DEFER;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return -EPROBE_DEFER;
+
+ dsp_priv->audiomix = dev_get_drvdata(&pdev->dev);
+ if (!dsp_priv->audiomix)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+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 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)
+{
+ struct imx8m_priv *dsp_priv = (struct imx8m_priv *)sdev->private;
+
+ imx_audiomix_dsp_start(dsp_priv->audiomix);
+
+ return 0;
+}
+
+static int imx8m_reset(struct snd_sof_dev *sdev) {
+
+ struct imx8m_priv *dsp_priv = (struct imx8m_priv *)sdev->private;
+ u32 pwrctl;
+
+ /* put DSP into reset and stall */
+ pwrctl = readl(dsp_priv->dap + IMX8M_DAP_PWRCTL);
+ pwrctl |= IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, dsp_priv->dap + IMX8M_DAP_PWRCTL);
+
+ /* keep reset asserted for 10 cycles */
+ usleep_range(1, 2);
+
+ imx_audiomix_dsp_stall(dsp_priv->audiomix);
+
+ /* take the DSP out of reset and keep stalled for FW loading */
+ pwrctl = readl(dsp_priv->dap + IMX8M_DAP_PWRCTL);
+ pwrctl &= ~IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, dsp_priv->dap + IMX8M_DAP_PWRCTL);
+
+ 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;
+ int i = 0;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ sdev->private = priv;
+ priv->dev = sdev->dev;
+ priv->sdev = sdev;
+
+ ret = imx8m_dsp_configure_audmix(priv);
+ if (ret < 0)
+ return ret;
+
+ /* power up device associated power domains */
+ priv->num_domains = of_count_phandle_with_args(np, "power-domains",
+ "#power-domain-cells");
+ if (priv->num_domains < 0) {
+ dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
+ return priv->num_domains;
+ }
+
+ /* power domain already enabled by PM core */
+ if (priv->num_domains == 1)
+ goto done_pm;
+
+ priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
+ sizeof(*priv->pd_dev), GFP_KERNEL);
+ if (!priv->pd_dev)
+ return -ENOMEM;
+
+ priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
+ sizeof(*priv->link), GFP_KERNEL);
+ if (!priv->link)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->num_domains; i++) {
+ priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
+ if (IS_ERR(priv->pd_dev[i])) {
+ ret = PTR_ERR(priv->pd_dev[i]);
+ goto exit_unroll_pm;
+ }
+ priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!priv->link[i]) {
+ ret = -ENOMEM;
+ dev_pm_domain_detach(priv->pd_dev[i], false);
+ goto exit_unroll_pm;
+ }
+ }
+
+done_pm:
+ priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(priv->ipc_dev)) {
+ ret = PTR_ERR(priv->ipc_dev);
+ goto exit_unroll_pm;
+ }
+
+ 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 = &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;
+ }
+
+ priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
+ if (!priv->dap ) {
+ dev_err(sdev->dev, "error: failed to map DAP debug memory area");
+ return -ENODEV;
+ }
+
+ 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;
+
+ for (i = 0; i < IMX8M_DSP_CLK_NUM; i++) {
+ priv->clks[i] = devm_clk_get(&pdev->dev, imx8m_dsp_clks[i]);
+ if (IS_ERR(priv->clks[i]))
+ priv->clks[i] = NULL;
+ }
+ /* TODO: handle clocks at PM suspend/resume time */
+ for (i = 0; i < IMX8M_DSP_CLK_NUM; i++)
+ clk_prepare_enable(priv->clks[i]);
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_unregister(priv->ipc_dev);
+exit_unroll_pm:
+ while (--i >= 0) {
+ device_link_del(priv->link[i]);
+ dev_pm_domain_detach(priv->pd_dev[i], false);
+ }
+ return ret;
+}
+
+static int imx8m_remove(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+ int i;
+
+ platform_device_unregister(priv->ipc_dev);
+
+ for (i = 0; i < priv->num_domains; i++) {
+ device_link_del(priv->link[i]);
+ dev_pm_domain_detach(priv->pd_dev[i], false);
+ }
+
+ for (i = 0; i < IMX8M_DSP_CLK_NUM; i++)
+ clk_disable_unprepare(priv->clks[i]);
+
+ 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 = "sai3",
+},
+};
+
+int imx8m_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+ int i;
+
+ for (i = 0; i < IMX8M_DSP_CLK_NUM; i++)
+ clk_prepare_enable(priv->clks[i]);
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(priv->dsp_ipc, i);
+
+ return 0;
+}
+
+int imx8m_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+ int i;
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(priv->dsp_ipc, i);
+
+ for (i = 0; i < IMX8M_DSP_CLK_NUM; i++)
+ clk_disable_unprepare(priv->clks[i]);
+
+ return 0;
+}
+
+static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev)
+{
+ return imx8m_resume(sdev);
+}
+
+static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev)
+{
+ return imx8m_suspend(sdev);
+}
+
+static int imx8m_dsp_resume(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+
+ if (priv->suspended) {
+ imx8m_resume(sdev);
+ priv->suspended = false;
+ }
+
+ return 0;
+}
+
+static int imx8m_dsp_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private;
+
+ if (!priv->suspended) {
+ imx8m_suspend(sdev);
+ priv->suspended = true;
+ }
+
+ return 0;
+}
+
+/* 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,
+ .reset = imx8m_reset,
+
+ /* 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 ESAI interface on i.MX8 */
+
+ .suspend = imx8m_dsp_suspend,
+ .resume = imx8m_dsp_resume,
+
+ .runtime_suspend = imx8m_dsp_runtime_suspend,
+ .runtime_resume = imx8m_dsp_runtime_resume,
+
+ .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/apl.c b/sound/soc/sof/intel/apl.c
index 8dc7a5558da4..336e48e77b01 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -53,6 +53,11 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.ipc_msg_data = hda_ipc_msg_data,
.ipc_pcm_params = hda_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = apl_dsp_debugfs,
.debug_map_count = ARRAY_SIZE(apl_dsp_debugfs),
@@ -97,6 +102,13 @@ const struct snd_sof_dsp_ops sof_apl_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,
+
+ /* ALSA HW info flags */
+ .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_apl_ops);
diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c
index 80e2826fb447..a0944da7cf6a 100644
--- a/sound/soc/sof/intel/bdw.c
+++ b/sound/soc/sof/intel/bdw.c
@@ -483,8 +483,11 @@ static int bdw_probe(struct snd_sof_dev *sdev)
/* register our IRQ */
sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
- if (sdev->ipc_irq < 0)
+ if (sdev->ipc_irq < 0) {
+ dev_err(sdev->dev, "error: failed to get IRQ at index %d\n",
+ desc->irqindex_host_ipc);
return sdev->ipc_irq;
+ }
dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
@@ -554,6 +557,11 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = bdw_debugfs,
.debug_map_count = ARRAY_SIZE(bdw_debugfs),
@@ -571,7 +579,14 @@ const struct snd_sof_dsp_ops sof_bdw_ops = {
/* DAI drivers */
.drv = bdw_dai,
- .num_drv = ARRAY_SIZE(bdw_dai)
+ .num_drv = ARRAY_SIZE(bdw_dai),
+
+ /* ALSA HW info flags */
+ .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_bdw_ops);
diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c
index 41008c974ac6..702576addd9a 100644
--- a/sound/soc/sof/intel/byt.c
+++ b/sound/soc/sof/intel/byt.c
@@ -494,6 +494,11 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = byt_debugfs,
.debug_map_count = ARRAY_SIZE(byt_debugfs),
@@ -512,6 +517,13 @@ const struct snd_sof_dsp_ops sof_tng_ops = {
/* DAI drivers */
.drv = byt_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .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_tng_ops);
@@ -600,8 +612,11 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev)
irq:
/* register our IRQ */
sdev->ipc_irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
- if (sdev->ipc_irq < 0)
+ if (sdev->ipc_irq < 0) {
+ dev_err(sdev->dev, "error: failed to get IRQ at index %d\n",
+ desc->irqindex_host_ipc);
return sdev->ipc_irq;
+ }
dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq);
ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq,
@@ -655,6 +670,11 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = byt_debugfs,
.debug_map_count = ARRAY_SIZE(byt_debugfs),
@@ -673,6 +693,13 @@ const struct snd_sof_dsp_ops sof_byt_ops = {
/* DAI drivers */
.drv = byt_dai,
.num_drv = 3, /* we have only 3 SSPs on byt*/
+
+ /* ALSA HW info flags */
+ .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_byt_ops);
@@ -714,6 +741,11 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
.ipc_msg_data = intel_ipc_msg_data,
.ipc_pcm_params = intel_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = cht_debugfs,
.debug_map_count = ARRAY_SIZE(cht_debugfs),
@@ -733,6 +765,13 @@ const struct snd_sof_dsp_ops sof_cht_ops = {
.drv = byt_dai,
/* all 6 SSPs may be available for cherrytrail */
.num_drv = ARRAY_SIZE(byt_dai),
+
+ /* ALSA HW info flags */
+ .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_cht_ops);
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 4ddd73762d81..9a19903befa2 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -211,6 +211,11 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.ipc_msg_data = hda_ipc_msg_data,
.ipc_pcm_params = hda_ipc_pcm_params,
+ /* machine driver */
+ .machine_check = sof_machine_check,
+ .machine_register = sof_machine_register,
+ .machine_unregister = sof_machine_unregister,
+
/* debug */
.debug_map = cnl_dsp_debugfs,
.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs),
@@ -255,6 +260,13 @@ const struct snd_sof_dsp_ops sof_cnl_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,
+
+ /* ALSA HW info flags */
+ .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_cnl_ops);
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index ce114df5e4fc..235be4fc0862 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -32,6 +32,42 @@ static int get_ext_windows(struct snd_sof_dev *sdev,
return 0;
}
+static int get_cc_info(struct snd_sof_dev *sdev,
+ struct sof_ipc_ext_data_hdr *ext_hdr)
+{
+ int ret;
+
+ struct sof_ipc_cc_version *cc =
+ container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
+
+ 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);
+
+ /* create read-only cc_version debugfs to store compiler version info */
+ /* use local copy of the cc_version to prevent data corruption */
+ if (sdev->first_boot) {
+ sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
+ GFP_KERNEL);
+
+ if (!sdev->cc_version)
+ return -ENOMEM;
+
+ memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
+ ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
+ cc->ext_hdr.hdr.size,
+ "cc_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");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/* parse the extended FW boot data structures from FW boot message */
int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
{
@@ -50,8 +86,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
/* read in ext structure */
- offset += sizeof(*ext_hdr);
- snd_sof_dsp_block_read(sdev, bar, offset,
+ snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
(void *)((u8 *)ext_data + sizeof(*ext_hdr)),
ext_hdr->hdr.size - sizeof(*ext_hdr));
@@ -61,13 +96,18 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
/* process structure data */
switch (ext_hdr->type) {
case SOF_IPC_EXT_DMA_BUFFER:
+ ret = 0;
break;
case SOF_IPC_EXT_WINDOW:
ret = get_ext_windows(sdev, ext_hdr);
break;
+ case SOF_IPC_EXT_CC_INFO:
+ ret = get_cc_info(sdev, ext_hdr);
+ break;
default:
dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
ext_hdr->type, ext_hdr->hdr.size);
+ ret = 0;
break;
}
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index 71410116add1..849c3bcdca9e 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -66,23 +66,11 @@ static int sof_nocodec_bes_setup(struct device *dev,
}
int sof_nocodec_setup(struct device *dev,
- struct snd_sof_pdata *sof_pdata,
- struct snd_soc_acpi_mach *mach,
- const struct sof_dev_desc *desc,
const struct snd_sof_dsp_ops *ops)
{
struct snd_soc_dai_link *links;
int ret;
- if (!mach)
- return -EINVAL;
-
- sof_pdata->drv_name = "sof-nocodec";
-
- mach->drv_name = "sof-nocodec";
- sof_pdata->fw_filename = desc->nocodec_fw_filename;
- sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
-
/* create dummy BE dai_links */
links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) *
ops->num_drv, GFP_KERNEL);
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 824d36fe59fd..97f4feb73368 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -381,6 +381,32 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev,
return 0;
}
+/* machine driver */
+static inline int
+snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->machine_register)
+ return sof_ops(sdev)->machine_register(sdev, pdata);
+
+ return 0;
+}
+
+static inline void
+snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->machine_unregister)
+ sof_ops(sdev)->machine_unregister(sdev, pdata);
+}
+
+static inline int
+snd_sof_machine_check(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->machine_check)
+ return sof_ops(sdev)->machine_check(sdev);
+
+ return 0;
+}
+
static inline const struct snd_sof_dsp_ops
*sof_get_ops(const struct sof_dev_desc *d,
const struct sof_ops_table mach_ops[], int asize)
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 2b876d497447..ed01523affba 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -34,7 +34,7 @@ static int create_page_table(struct snd_pcm_substream *substream,
if (!spcm)
return -EINVAL;
- return snd_sof_create_page_table(sdev, dmab,
+ return snd_sof_create_page_table(sdev->dev, dmab,
spcm->stream[stream].page_table.area, size);
}
@@ -435,6 +435,7 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
struct snd_sof_pcm *spcm;
struct snd_soc_tplg_stream_caps *caps;
int ret;
@@ -464,11 +465,8 @@ static int sof_pcm_open(struct snd_pcm_substream *substream)
le32_to_cpu(caps->period_size_min));
/* set runtime config */
- runtime->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;
+ runtime->hw.info = ops->hw_info; /* platform-specific */
+
runtime->hw.formats = le64_to_cpu(caps->formats);
runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min);
runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max);
@@ -691,6 +689,19 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
case SOF_DAI_INTEL_ALH:
/* do nothing for ALH dai_link */
break;
+ case SOF_DAI_IMX_ESAI:
+ channels->min = dai->dai_config->esai.tdm_slots;
+ channels->max = dai->dai_config->esai.tdm_slots;
+
+ break;
+ case SOF_DAI_IMX_SAI:
+ channels->min = dai->dai_config->sai.tdm_slots;
+ channels->max = dai->dai_config->sai.tdm_slots;
+
+ dev_dbg(sdev->dev,
+ "channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
default:
dev_err(sdev->dev, "error: invalid DAI type %d\n",
dai->dai_config->type);
@@ -724,14 +735,6 @@ static int sof_pcm_probe(struct snd_soc_component *component)
return ret;
}
- /*
- * Some platforms in SOF, ex: BYT, may not have their platform PM
- * callbacks set. Increment the usage count so as to
- * prevent the device from entering runtime suspend.
- */
- if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
- pm_runtime_get_noresume(sdev->dev);
-
return ret;
}
@@ -747,7 +750,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
struct snd_sof_pdata *plat_data = sdev->pdata;
const char *drv_name;
- drv_name = plat_data->machine->drv_name;
+
+ if (plat_data->machine)
+ drv_name = plat_data->machine->drv_name;
+ else
+ drv_name = plat_data->machine_drv_name;
pd->name = "sof-audio-component";
pd->probe = sof_pcm_probe;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 128680b09c20..789ed6a28ed7 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -290,6 +290,9 @@ static int sof_resume(struct device *dev, bool runtime_resume)
return ret;
}
+ if (!runtime_resume && pm_runtime_suspended(sdev->dev))
+ return 0;
+
sdev->fw_state = SOF_FW_BOOT_PREPARE;
/* load the firmware */
diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c
index ea7b8b895412..9b77916d8aaf 100644
--- a/sound/soc/sof/sof-acpi-dev.c
+++ b/sound/soc/sof/sof-acpi-dev.c
@@ -39,6 +39,7 @@ static const struct sof_dev_desc sof_acpi_haswell_desc = {
.chip_info = &hsw_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-hsw.ri",
.nocodec_fw_filename = "sof-hsw.ri",
.nocodec_tplg_filename = "sof-hsw-nocodec.tplg",
.ops = &sof_hsw_ops,
@@ -56,6 +57,7 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = {
.chip_info = &bdw_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-bdw.ri",
.nocodec_fw_filename = "sof-bdw.ri",
.nocodec_tplg_filename = "sof-bdw-nocodec.tplg",
.ops = &sof_bdw_ops,
@@ -75,6 +77,7 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = {
.chip_info = &byt_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-byt.ri",
.nocodec_fw_filename = "sof-byt.ri",
.nocodec_tplg_filename = "sof-byt-nocodec.tplg",
.ops = &sof_byt_ops,
@@ -90,6 +93,7 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = {
.chip_info = &byt_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-byt.ri",
.nocodec_fw_filename = "sof-byt.ri",
.nocodec_tplg_filename = "sof-byt-nocodec.tplg",
.ops = &sof_byt_ops,
@@ -105,6 +109,7 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = {
.chip_info = &cht_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-cht.ri",
.nocodec_fw_filename = "sof-cht.ri",
.nocodec_tplg_filename = "sof-cht-nocodec.tplg",
.ops = &sof_cht_ops,
@@ -164,7 +169,11 @@ static int sof_acpi_probe(struct platform_device *pdev)
mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL);
if (!mach)
return -ENOMEM;
- ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops);
+
+ mach->drv_name = "sof-nocodec";
+ sof_pdata->fw_filename = desc->nocodec_fw_filename;
+ sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+ ret = sof_nocodec_setup(dev, ops);
if (ret < 0)
return ret;
#else
diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c
index 28a9692974e5..befa35ba492f 100644
--- a/sound/soc/sof/sof-of-dev.c
+++ b/sound/soc/sof/sof-of-dev.c
@@ -13,18 +13,40 @@
#include "ops.h"
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)
static struct sof_dev_desc sof_of_imx8qxp_desc = {
.default_fw_path = "imx/sof",
.default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8x.ri",
.nocodec_fw_filename = "sof-imx8.ri",
.nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
+ .ops = &sof_imx8x_ops,
+};
+
+static struct sof_dev_desc sof_of_imx8qm_desc = {
+ .default_fw_path = "imx/sof",
+ .default_tplg_path = "imx/sof-tplg",
+ .default_fw_filename = "sof-imx8.ri",
+ .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
.ops = &sof_imx8_ops,
};
#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_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,
@@ -36,7 +58,33 @@ static void sof_of_probe_complete(struct device *dev)
/* allow runtime_pm */
pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+
+int sof_of_parse(struct platform_device *pdev)
+{
+ struct snd_sof_pdata *sof_pdata = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ /* firmware-name is optional in DT */
+ of_property_read_string(np, "firmware-name", &sof_pdata->fw_filename);
+
+ ret = of_property_read_string(np, "tplg-name",
+ &sof_pdata->tplg_filename);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_read_string(np, "machine-drv-name",
+ &sof_pdata->machine_drv_name);
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int sof_of_probe(struct platform_device *pdev)
@@ -44,7 +92,9 @@ static int sof_of_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct sof_dev_desc *desc;
/*TODO: create a generic snd_soc_xxx_mach */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)
struct snd_soc_acpi_mach *mach;
+#endif
struct snd_sof_pdata *sof_pdata;
const struct snd_sof_dsp_ops *ops;
int ret;
@@ -55,6 +105,8 @@ static int sof_of_probe(struct platform_device *pdev)
if (!sof_pdata)
return -ENOMEM;
+ platform_set_drvdata(pdev, sof_pdata);
+
desc = device_get_match_data(dev);
if (!desc)
return -ENODEV;
@@ -72,18 +124,17 @@ static int sof_of_probe(struct platform_device *pdev)
mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL);
if (!mach)
return -ENOMEM;
- ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops);
+
+ mach->drv_name = "sof-nocodec";
+ sof_pdata->fw_filename = desc->nocodec_fw_filename;
+ sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+ ret = sof_nocodec_setup(dev, ops);
if (ret < 0)
return ret;
-#else
- /* TODO: implement case where we actually have a codec */
- return -ENODEV;
#endif
- if (mach)
- mach->mach_params.platform = dev_name(dev);
-
- sof_pdata->machine = mach;
+ /* TODO: replace machine with info from DT */
+ sof_pdata->machine = NULL;
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
sof_pdata->platform = dev_name(dev);
@@ -92,6 +143,16 @@ static int sof_of_probe(struct platform_device *pdev)
sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path;
sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path;
+ ret = sof_of_parse(pdev);
+ if (ret < 0) {
+ dev_err(dev, "Could not parse SOF OF DSP node\n");
+ return ret;
+ }
+
+ /* use default fw filename if none provided in DT */
+ if (!sof_pdata->fw_filename)
+ sof_pdata->fw_filename = desc->default_fw_filename;
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)
/* set callback to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_of_probe_complete;
@@ -123,7 +184,12 @@ static int sof_of_remove(struct platform_device *pdev)
static const struct of_device_id sof_of_ids[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8)
{ .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 3f79cd03507c..6b6663744b04 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -40,6 +40,7 @@ static const struct sof_dev_desc bxt_desc = {
.chip_info = &apl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-apl.ri",
.nocodec_fw_filename = "sof-apl.ri",
.nocodec_tplg_filename = "sof-apl-nocodec.tplg",
.ops = &sof_apl_ops,
@@ -58,6 +59,7 @@ static const struct sof_dev_desc glk_desc = {
.chip_info = &apl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-glk.ri",
.nocodec_fw_filename = "sof-glk.ri",
.nocodec_tplg_filename = "sof-glk-nocodec.tplg",
.ops = &sof_apl_ops,
@@ -86,6 +88,7 @@ static const struct sof_dev_desc tng_desc = {
.chip_info = &tng_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-byt.ri",
.nocodec_fw_filename = "sof-byt.ri",
.nocodec_tplg_filename = "sof-byt.tplg",
.ops = &sof_tng_ops,
@@ -104,6 +107,7 @@ static const struct sof_dev_desc cnl_desc = {
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-cnl.ri",
.nocodec_fw_filename = "sof-cnl.ri",
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
@@ -113,7 +117,7 @@ static const struct sof_dev_desc cnl_desc = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
static const struct sof_dev_desc cfl_desc = {
- .machines = snd_soc_acpi_intel_cnl_machines,
+ .machines = snd_soc_acpi_intel_cfl_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
@@ -122,7 +126,8 @@ static const struct sof_dev_desc cfl_desc = {
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-cnl.ri",
+ .default_fw_filename = "sof-cfl.ri",
+ .nocodec_fw_filename = "sof-cfl.ri",
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
.arch_ops = &sof_xtensa_arch_ops
@@ -133,7 +138,7 @@ static const struct sof_dev_desc cfl_desc = {
IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H)
static const struct sof_dev_desc cml_desc = {
- .machines = snd_soc_acpi_intel_cnl_machines,
+ .machines = snd_soc_acpi_intel_cml_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
@@ -142,7 +147,8 @@ static const struct sof_dev_desc cml_desc = {
.chip_info = &cnl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
- .nocodec_fw_filename = "sof-cnl.ri",
+ .default_fw_filename = "sof-cml.ri",
+ .nocodec_fw_filename = "sof-cml.ri",
.nocodec_tplg_filename = "sof-cnl-nocodec.tplg",
.ops = &sof_cnl_ops,
.arch_ops = &sof_xtensa_arch_ops
@@ -160,6 +166,7 @@ static const struct sof_dev_desc icl_desc = {
.chip_info = &icl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-icl.ri",
.nocodec_fw_filename = "sof-icl.ri",
.nocodec_tplg_filename = "sof-icl-nocodec.tplg",
.ops = &sof_cnl_ops,
@@ -214,6 +221,7 @@ static const struct sof_dev_desc tgl_desc = {
.chip_info = &tgl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-tgl.ri",
.nocodec_fw_filename = "sof-tgl.ri",
.nocodec_tplg_filename = "sof-tgl-nocodec.tplg",
.ops = &sof_cnl_ops,
@@ -232,6 +240,7 @@ static const struct sof_dev_desc ehl_desc = {
.chip_info = &ehl_chip_info,
.default_fw_path = "intel/sof",
.default_tplg_path = "intel/sof-tplg",
+ .default_fw_filename = "sof-ehl.ri",
.nocodec_fw_filename = "sof-ehl.ri",
.nocodec_tplg_filename = "sof-ehl-nocodec.tplg",
.ops = &sof_cnl_ops,
@@ -306,7 +315,10 @@ static int sof_pci_probe(struct pci_dev *pci,
ret = -ENOMEM;
goto release_regions;
}
- ret = sof_nocodec_setup(dev, sof_pdata, mach, desc, ops);
+ mach->drv_name = "sof-nocodec";
+ sof_pdata->fw_filename = desc->nocodec_fw_filename;
+ sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
+ ret = sof_nocodec_setup(dev, ops);
if (ret < 0)
goto release_regions;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 7b329bd99674..f04550c7ca09 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -202,9 +202,19 @@ struct snd_sof_dsp_ops {
int (*get_window_offset)(struct snd_sof_dev *sdev,
u32 id);/* mandatory for common loader code */
+ /* machine driver ops */
+ int (*machine_register)(struct snd_sof_dev *sdev,
+ void *pdata); /* optional */
+ void (*machine_unregister)(struct snd_sof_dev *sdev,
+ void *pdata); /* optional */
+ int (*machine_check)(struct snd_sof_dev *sdev); /* optional */
+
/* DAI ops */
struct snd_soc_dai_driver *drv;
int num_drv;
+
+ /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
+ u32 hw_info;
};
/* DSP architecture specific callbacks for oops and stack dumps */
@@ -414,6 +424,7 @@ struct snd_sof_dev {
struct snd_dma_buffer dmab_bdl;
struct sof_ipc_fw_ready fw_ready;
struct sof_ipc_fw_version fw_version;
+ struct sof_ipc_cc_version *cc_version;
/* topology */
struct snd_soc_tplg_ops *tplg_ops;
@@ -467,10 +478,14 @@ int snd_sof_suspend(struct device *dev);
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
-int snd_sof_create_page_table(struct snd_sof_dev *sdev,
+int snd_sof_create_page_table(struct device *dev,
struct snd_dma_buffer *dmab,
unsigned char *page_table, size_t size);
+int sof_machine_register(struct snd_sof_dev *sdev, void *pdata);
+void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
+int sof_machine_check(struct snd_sof_dev *sdev);
+
/*
* Firmware loading.
*/
@@ -551,8 +566,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc,
* There is no snd_sof_free_topology since topology components will
* be freed by snd_soc_unregister_component,
*/
-int snd_sof_init_topology(struct snd_sof_dev *sdev,
- struct snd_soc_tplg_ops *ops);
int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file);
int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
struct snd_sof_widget *swidget);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index fa299e078156..bbe9069e8464 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -406,6 +406,7 @@ 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},
+ {"POST_PROCESS", SOF_PROCESS_PP, SOF_COMP_PP},
};
static enum sof_ipc_process_type find_process(const char *name)
@@ -799,6 +800,21 @@ static const struct sof_topology_token dmic_tokens[] = {
};
+/* ESAI */
+static const struct sof_topology_token esai_tokens[] = {
+ {SOF_TKN_IMX_ESAI_MCLK_ID,
+ SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_esai_params, mclk_id), 0},
+};
+
+/* SAI */
+static const struct sof_topology_token sai_tokens[] = {
+ {SOF_TKN_IMX_SAI_MCLK_ID,
+ SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,
+ offsetof(struct sof_ipc_dai_sai_params, mclk_id), 0},
+};
+
+
/*
* DMIC PDM Tokens
* SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token
@@ -2526,8 +2542,66 @@ static int sof_link_sai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_hw_config *hw_config,
struct sof_ipc_dai_config *config)
{
- /*TODO: Add implementation */
- return 0;
+ 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;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->sai, 0, sizeof(struct sof_ipc_dai_sai_params));
+ config->hdr.size = size;
+
+ ret = sof_parse_tokens(scomp, &config->sai, sai_tokens,
+ ARRAY_SIZE(sai_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(sdev->dev, "error: parse sai tokens failed %d\n",
+ le32_to_cpu(private->size));
+ return ret;
+ }
+
+ config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
+ config->sai.mclk_direction = hw_config->mclk_direction;
+
+ config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
+ config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots);
+ config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots);
+
+ dev_info(sdev->dev,
+ "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
+ config->dai_index, config->format,
+ config->sai.mclk_rate, config->sai.tdm_slot_width,
+ config->sai.tdm_slots, config->sai.mclk_id);
+
+ if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) {
+ dev_err(sdev->dev, "error: invalid channel count for SAI%d\n",
+ config->dai_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(sdev->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)
+ dev_err(sdev->dev, "error: failed to save DAI config for SAI%d\n",
+ config->dai_index);
+
+ return ret;
}
static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
@@ -2536,8 +2610,66 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_hw_config *hw_config,
struct sof_ipc_dai_config *config)
{
- /*TODO: Add implementation */
- return 0;
+ 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;
+
+ /* handle master/slave and inverted clocks */
+ sof_dai_set_format(hw_config, config);
+
+ /* init IPC */
+ memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params));
+ config->hdr.size = size;
+
+ ret = sof_parse_tokens(scomp, &config->esai, esai_tokens,
+ ARRAY_SIZE(esai_tokens), private->array,
+ le32_to_cpu(private->size));
+ if (ret != 0) {
+ dev_err(sdev->dev, "error: parse esai tokens failed %d\n",
+ le32_to_cpu(private->size));
+ return ret;
+ }
+
+ config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);
+ config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);
+ config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);
+ config->esai.mclk_direction = hw_config->mclk_direction;
+ config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);
+ config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);
+ config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);
+ config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);
+
+ dev_info(sdev->dev,
+ "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
+ config->dai_index, config->format,
+ config->esai.mclk_rate, config->esai.tdm_slot_width,
+ config->esai.tdm_slots, config->esai.mclk_id);
+
+ if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {
+ dev_err(sdev->dev, "error: invalid channel count for ESAI%d\n",
+ config->dai_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(sdev->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)
+ dev_err(sdev->dev, "error: failed to save DAI config for ESAI%d\n",
+ config->dai_index);
+
+ return ret;
}
static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
@@ -2972,7 +3104,9 @@ found:
case SOF_DAI_INTEL_SSP:
case SOF_DAI_INTEL_DMIC:
case SOF_DAI_INTEL_ALH:
- /* no resource needs to be released for SSP, DMIC and ALH */
+ case SOF_DAI_IMX_SAI:
+ case SOF_DAI_IMX_ESAI:
+ /* no resource needs to be released for all cases above */
break;
case SOF_DAI_INTEL_HDA:
ret = sof_link_hda_unload(sdev, link);
@@ -3303,15 +3437,6 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
.bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops),
};
-int snd_sof_init_topology(struct snd_sof_dev *sdev,
- struct snd_soc_tplg_ops *ops)
-{
- /* TODO: support linked list of topologies */
- sdev->tplg_ops = ops;
- return 0;
-}
-EXPORT_SYMBOL(snd_sof_init_topology);
-
int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file)
{
const struct firmware *fw;
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index fd6f5913782b..a775c6b21b55 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -244,8 +244,8 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev)
}
/* create compressed page table for audio firmware */
- ret = snd_sof_create_page_table(sdev, &sdev->dmatb, sdev->dmatp.area,
- sdev->dmatb.bytes);
+ ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb,
+ sdev->dmatp.area, sdev->dmatb.bytes);
if (ret < 0)
goto table_err;
diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c
index 2ac4c3da0320..9831eb57df6c 100644
--- a/sound/soc/sof/utils.c
+++ b/sound/soc/sof/utils.c
@@ -10,6 +10,7 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/platform_device.h>
+#include <asm/unaligned.h>
#include <sound/soc.h>
#include <sound/sof.h>
#include "sof-priv.h"
@@ -110,3 +111,62 @@ void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest,
memcpy_fromio(dest, src, size);
}
EXPORT_SYMBOL(sof_block_read);
+
+/*
+ * Generic buffer page table creation.
+ * Take the each physical page address and drop the least significant unused
+ * bits from each (based on PAGE_SIZE). Then pack valid page address bits
+ * into compressed page table.
+ */
+
+int snd_sof_create_page_table(struct device *dev,
+ struct snd_dma_buffer *dmab,
+ unsigned char *page_table, size_t size)
+{
+ int i, pages;
+
+ pages = snd_sgbuf_aligned_pages(size);
+
+ dev_dbg(dev, "generating page table for %p size 0x%zx pages %d\n",
+ dmab->area, size, pages);
+
+ for (i = 0; i < pages; i++) {
+ /*
+ * The number of valid address bits for each page is 20.
+ * idx determines the byte position within page_table
+ * where the current page's address is stored
+ * in the compressed page_table.
+ * This can be calculated by multiplying the page number by 2.5.
+ */
+ u32 idx = (5 * i) >> 1;
+ u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
+ u8 *pg_table;
+
+ dev_vdbg(dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
+
+ pg_table = (u8 *)(page_table + idx);
+
+ /*
+ * pagetable compression:
+ * byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
+ * ___________pfn 0__________ __________pfn 1___________ _pfn 2...
+ * .... .... .... .... .... .... .... .... .... .... ....
+ * It is created by:
+ * 1. set current location to 0, PFN index i to 0
+ * 2. put pfn[i] at current location in Little Endian byte order
+ * 3. calculate an intermediate value as
+ * x = (pfn[i+1] << 4) | (pfn[i] & 0xf)
+ * 4. put x at offset (current location + 2) in LE byte order
+ * 5. increment current location by 5 bytes, increment i by 2
+ * 6. continue to (2)
+ */
+ if (i & 1)
+ put_unaligned_le32((pg_table[0] & 0xf) | pfn << 4,
+ pg_table);
+ else
+ put_unaligned_le32(pfn, pg_table);
+ }
+
+ return pages;
+}
+EXPORT_SYMBOL(snd_sof_create_page_table);