summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/sof.h3
-rw-r--r--include/sound/sof/dai-intel.h20
-rw-r--r--include/sound/sof/info.h26
-rw-r--r--include/sound/sof/topology.h16
-rw-r--r--include/sound/sof/trace.h2
-rw-r--r--include/uapi/sound/sof/abi.h2
-rw-r--r--include/uapi/sound/sof/ext_manifest.h91
-rw-r--r--include/uapi/sound/sof/tokens.h8
-rw-r--r--sound/soc/sof/intel/hda-loader.c9
-rw-r--r--sound/soc/sof/loader.c226
-rw-r--r--sound/soc/sof/topology.c323
11 files changed, 568 insertions, 158 deletions
diff --git a/include/sound/sof.h b/include/sound/sof.h
index a0cbca021230..969f554b0b7d 100644
--- a/include/sound/sof.h
+++ b/include/sound/sof.h
@@ -27,6 +27,9 @@ struct snd_sof_pdata {
struct device *dev;
+ /* indicate how many first bytes shouldn't be loaded into DSP memory. */
+ size_t fw_offset;
+
/*
* notification callback used if the hardware initialization
* can take time or is handled in a workqueue. This callback
diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h
index 04e48227f542..896db2243d87 100644
--- a/include/sound/sof/dai-intel.h
+++ b/include/sound/sof/dai-intel.h
@@ -49,6 +49,9 @@
/* bclk idle */
#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5)
+/* DMIC max. four controllers for eight microphone channels */
+#define SOF_DAI_INTEL_DMIC_NUM_CTRL 4
+
/* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */
struct sof_ipc_dai_ssp_params {
struct sof_ipc_hdr hdr;
@@ -85,15 +88,19 @@ struct sof_ipc_dai_ssp_params {
struct sof_ipc_dai_hda_params {
struct sof_ipc_hdr hdr;
uint32_t link_dma_ch;
+ uint32_t rate;
+ uint32_t channels;
} __packed;
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
struct sof_ipc_dai_alh_params {
struct sof_ipc_hdr hdr;
uint32_t stream_id;
+ uint32_t rate;
+ uint32_t channels;
/* reserved for future use */
- uint32_t reserved[15];
+ uint32_t reserved[13];
} __packed;
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */
@@ -135,7 +142,7 @@ struct sof_ipc_dai_dmic_pdm_ctrl {
* version number used in configuration data is checked vs. version used by
* device driver src/drivers/dmic.c need to match. It is incremented from
* initial value 1 if updates done for the to driver would alter the operation
- * of the microhone.
+ * of the microphone.
*
* Note: The microphone clock (pdmclk_min, pdmclk_max, duty_min, duty_max)
* parameters need to be set as defined in microphone data sheet. E.g. clock
@@ -170,12 +177,13 @@ struct sof_ipc_dai_dmic_params {
uint32_t fifo_fs; /**< FIFO sample rate in Hz (8000..96000) */
uint32_t reserved_1; /**< Reserved */
uint16_t fifo_bits; /**< FIFO word length (16 or 32) */
- uint16_t reserved_2; /**< Reserved */
+ uint16_t fifo_bits_b; /**< Deprecated since firmware ABI 3.0.1 */
uint16_t duty_min; /**< Min. mic clock duty cycle in % (20..80) */
uint16_t duty_max; /**< Max. mic clock duty cycle in % (min..80) */
- uint32_t num_pdm_active; /**< Number of active pdm controllers */
+ uint32_t num_pdm_active; /**< Number of active pdm controllers. */
+ /**< Range is 1..SOF_DAI_INTEL_DMIC_NUM_CTRL */
uint32_t wake_up_time; /**< Time from clock start to data (us) */
uint32_t min_clock_on_time; /**< Min. time that clk is kept on (us) */
@@ -184,8 +192,8 @@ struct sof_ipc_dai_dmic_params {
/* reserved for future use */
uint32_t reserved[5];
- /**< variable number of pdm controller config */
- struct sof_ipc_dai_dmic_pdm_ctrl pdm[0];
+ /**< PDM controllers configuration */
+ struct sof_ipc_dai_dmic_pdm_ctrl pdm[SOF_DAI_INTEL_DMIC_NUM_CTRL];
} __packed;
#endif
diff --git a/include/sound/sof/info.h b/include/sound/sof/info.h
index 438a11fcf272..d5eff3179a39 100644
--- a/include/sound/sof/info.h
+++ b/include/sound/sof/info.h
@@ -31,6 +31,8 @@ enum sof_ipc_ext_data {
SOF_IPC_EXT_UNUSED = 0,
SOF_IPC_EXT_WINDOW = 1,
SOF_IPC_EXT_CC_INFO = 2,
+ SOF_IPC_EXT_PROBE_INFO = 3,
+ SOF_IPC_EXT_USER_ABI_INFO = 4,
};
/* FW version - SOF_IPC_GLB_VERSION */
@@ -109,9 +111,27 @@ struct sof_ipc_cc_version {
/* reserved for future use */
uint32_t reserved[4];
- char name[16]; /* null terminated compiler name */
- char optim[4]; /* null terminated compiler -O flag value */
- char desc[]; /* null terminated compiler description */
+ uint8_t name[16]; /* null terminated compiler name */
+ uint8_t optim[4]; /* null terminated compiler -O flag value */
+ uint8_t desc[32]; /* null terminated compiler description */
} __packed;
+/* extended data: Probe setup */
+struct sof_ipc_probe_support {
+ struct sof_ipc_ext_data_hdr ext_hdr;
+
+ uint32_t probe_points_max;
+ uint32_t injection_dmas_max;
+
+ /* reserved for future use */
+ uint32_t reserved[2];
+} __packed;
+
+/* extended data: user abi version(s) */
+struct sof_ipc_user_abi_version {
+ struct sof_ipc_ext_data_hdr ext_hdr;
+
+ uint32_t abi_dbg_version;
+} __packed;
+
#endif
diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h
index 402e0250c508..08267d284edc 100644
--- a/include/sound/sof/topology.h
+++ b/include/sound/sof/topology.h
@@ -37,6 +37,7 @@ enum sof_comp_type {
SOF_COMP_SELECTOR, /**< channel selector component */
SOF_COMP_DEMUX,
SOF_COMP_ASRC, /**< Asynchronous sample rate converter */
+ SOF_COMP_DCBLOCK,
/* keep FILEREAD/FILEWRITE as the last ones */
SOF_COMP_FILEREAD = 10000, /**< host test based file IO */
SOF_COMP_FILEWRITE = 10001, /**< host test based file IO */
@@ -75,11 +76,23 @@ struct sof_ipc_comp {
#define SOF_MEM_CAPS_CACHE (1 << 6) /**< cacheable */
#define SOF_MEM_CAPS_EXEC (1 << 7) /**< executable */
+/*
+ * overrun will cause ring buffer overwrite, instead of XRUN.
+ */
+#define SOF_BUF_OVERRUN_PERMITTED BIT(0)
+
+/*
+ * underrun will cause readback of 0s, instead of XRUN.
+ */
+#define SOF_BUF_UNDERRUN_PERMITTED BIT(1)
+
/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */
struct sof_ipc_buffer {
struct sof_ipc_comp comp;
uint32_t size; /**< buffer size in bytes */
uint32_t caps; /**< SOF_MEM_CAPS_ */
+ uint32_t flags; /**< SOF_BUF_ flags defined above */
+ uint32_t reserved; /**< reserved for future use */
} __packed;
/* generic component config data - must always be after struct sof_ipc_comp */
@@ -206,6 +219,7 @@ enum sof_ipc_process_type {
SOF_PROCESS_CHAN_SELECTOR, /**< Channel Selector */
SOF_PROCESS_MUX,
SOF_PROCESS_DEMUX,
+ SOF_PROCESS_DCBLOCK,
};
/* generic "effect", "codec" or proprietary processing component */
@@ -218,7 +232,7 @@ struct sof_ipc_comp_process {
/* reserved for future use */
uint32_t reserved[7];
- unsigned char data[0];
+ uint8_t data[0];
} __packed;
/* frees components, buffers and pipelines
diff --git a/include/sound/sof/trace.h b/include/sound/sof/trace.h
index fda6e8f6ead4..8056f214946d 100644
--- a/include/sound/sof/trace.h
+++ b/include/sound/sof/trace.h
@@ -72,7 +72,7 @@ struct sof_ipc_dma_trace_posn {
struct sof_ipc_panic_info {
struct sof_ipc_hdr hdr;
uint32_t code; /* SOF_IPC_PANIC_ */
- char filename[SOF_TRACE_FILENAME_SIZE];
+ uint8_t filename[SOF_TRACE_FILENAME_SIZE];
uint32_t linenum;
} __packed;
diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h
index 5995b79d6df1..d54be303090f 100644
--- a/include/uapi/sound/sof/abi.h
+++ b/include/uapi/sound/sof/abi.h
@@ -26,7 +26,7 @@
/* SOF ABI version major, minor and patch numbers */
#define SOF_ABI_MAJOR 3
-#define SOF_ABI_MINOR 13
+#define SOF_ABI_MINOR 16
#define SOF_ABI_PATCH 0
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
diff --git a/include/uapi/sound/sof/ext_manifest.h b/include/uapi/sound/sof/ext_manifest.h
new file mode 100644
index 000000000000..d49c47d08c7f
--- /dev/null
+++ b/include/uapi/sound/sof/ext_manifest.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2020 Intel Corporation. All rights reserved.
+ */
+
+/*
+ * Extended manifest is a place to store metadata about firmware, known during
+ * compilation time - for example firmware version or used compiler.
+ * Given information are read on host side before firmware startup.
+ * This part of output binary is not signed.
+ */
+
+#ifndef __SOF_FIRMWARE_EXT_MANIFEST_H__
+#define __SOF_FIRMWARE_EXT_MANIFEST_H__
+
+#include <linux/const.h>
+#include <sound/sof/info.h>
+
+/* In ASCII `XMan` */
+#define SOF_EXT_MAN_MAGIC_NUMBER 0x6e614d58
+
+/* Build u32 number in format MMmmmppp */
+#define SOF_EXT_MAN_BUILD_VERSION(MAJOR, MINOR, PATH) ((uint32_t)( \
+ ((MAJOR) << 24) | \
+ ((MINOR) << 12) | \
+ (PATH)))
+
+/* check extended manifest version consistency */
+#define SOF_EXT_MAN_VERSION_INCOMPATIBLE(host_ver, cli_ver) ( \
+ ((host_ver) & GENMASK(31, 24)) != \
+ ((cli_ver) & GENMASK(31, 24)))
+
+/* used extended manifest header version */
+#define SOF_EXT_MAN_VERSION SOF_EXT_MAN_BUILD_VERSION(1, 0, 0)
+
+/* extended manifest header, deleting any field breaks backward compatibility */
+struct sof_ext_man_header {
+ uint32_t magic; /*< identification number, */
+ /*< EXT_MAN_MAGIC_NUMBER */
+ uint32_t full_size; /*< [bytes] full size of ext_man, */
+ /*< (header + content + padding) */
+ uint32_t header_size; /*< [bytes] makes header extensionable, */
+ /*< after append new field to ext_man header */
+ /*< then backward compatible won't be lost */
+ uint32_t header_version; /*< value of EXT_MAN_VERSION */
+ /*< not related with following content */
+ uint8_t elements[]; /*< list of ext_man_elem_* elements */
+} __packed;
+
+/* Now define extended manifest elements */
+
+/* Extended manifest elements types */
+enum sof_ext_man_elem_type {
+ SOF_EXT_MAN_ELEM_FW_VERSION = 0,
+ SOF_EXT_MAN_ELEM_WINDOW = SOF_IPC_EXT_WINDOW,
+ SOF_EXT_MAN_ELEM_CC_VERSION = SOF_IPC_EXT_CC_INFO,
+};
+
+/* extended manifest element header */
+struct sof_ext_man_elem_header {
+ uint32_t type; /*< SOF_EXT_MAN_ELEM_ */
+ uint32_t size; /*< in bytes, including header size */
+ uint8_t blob[]; /*< type dependent content */
+} __packed;
+
+/* FW version */
+struct sof_ext_man_fw_version {
+ struct sof_ext_man_elem_header hdr;
+ /* use sof_ipc struct because of code re-use */
+ struct sof_ipc_fw_version version;
+ uint32_t flags;
+} __packed;
+
+/* extended data memory windows for IPC, trace and debug */
+struct sof_ext_man_window {
+ struct sof_ext_man_elem_header hdr;
+ /* use sof_ipc struct because of code re-use */
+ struct sof_ipc_window ipc_window;
+} __packed;
+
+/* Used C compiler description */
+struct sof_ext_man_cc_version {
+ struct sof_ext_man_elem_header hdr;
+ /* use sof_ipc struct because of code re-use */
+ struct sof_ipc_cc_version cc_version;
+} __packed;
+
+#endif /* __SOF_FIRMWARE_EXT_MANIFEST_H__ */
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h
index 2a25cd8da503..5941e2eb1588 100644
--- a/include/uapi/sound/sof/tokens.h
+++ b/include/uapi/sound/sof/tokens.h
@@ -126,4 +126,12 @@
#define SOF_TKN_MUTE_LED_USE 1300
#define SOF_TKN_MUTE_LED_DIRECTION 1301
+/* ALH */
+#define SOF_TKN_INTEL_ALH_RATE 1400
+#define SOF_TKN_INTEL_ALH_CH 1401
+
+/* HDA */
+#define SOF_TKN_INTEL_HDA_RATE 1500
+#define SOF_TKN_INTEL_HDA_CH 1501
+
#endif
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index e1550ccd0a49..1beaaf5879e2 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -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/loader.c b/sound/soc/sof/loader.c
index 64af08293daa..8be30cd5e038 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -12,20 +12,29 @@
#include <linux/firmware.h>
#include <sound/sof.h>
+#include <uapi/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 (sdev->info_window) {
+ if (memcmp(sdev->info_window, w, w_size)) {
+ dev_err(sdev->dev, "error: mistmatch between window descriptor from extended manifest and mailbox");
+ return -EINVAL;
+ }
+ return 0;
+ }
if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
return -EINVAL;
/* 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,151 @@ 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;
+
+ 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_ipc_ext_data_hdr *w_ipc;
+ const struct sof_ext_man_window *w;
+
+ w = container_of(hdr, struct sof_ext_man_window, hdr);
+ w_ipc = (const struct sof_ipc_ext_data_hdr *)&w->ipc_window;
+
+ return get_ext_windows(sdev, w_ipc);
+}
+
+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;
+ const struct sof_ipc_ext_data_hdr *cc_version;
+
+ cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
+ cc_version = (const struct sof_ipc_ext_data_hdr *)&cc->cc_version;
+
+ return get_cc_info(sdev, cc_version);
+}
+
+static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
+{
+ const struct sof_ext_man_header *head = (void *)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) {
+ dev_err(sdev->dev, "error: exception while reading firmware extended manifest, code %d\n",
+ (int)ext_man_size);
+ return ext_man_size;
+ } else if (!ext_man_size) {
+ dev_err(sdev->dev, "error: can't parse extended manifest when it's not present\n");
+ return -EINVAL;
+ }
+
+ /* 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);
+ break;
+ }
+
+ /* 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);
+ break;
+ }
+
+ remaining -= elem_hdr->size;
+ iptr += elem_hdr->size;
+ }
+
+ if (remaining) {
+ dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
/*
* IPC Firmware ready.
*/
@@ -379,12 +541,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 +562,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 +575,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 +585,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 +635,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 +653,33 @@ 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_ext_man_size(plat_data->fw);
+ if (ext_man_size > 0) {
+ ret = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
+
+ /* when no error occurred, drop extended manifest */
+ if (!ret)
+ plat_data->fw_offset = ext_man_size;
+ else
+ dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
+ fw_filename, ret);
+ } 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 +696,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 +710,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/topology.c b/sound/soc/sof/topology.c
index fe8ba3e05e08..e88ffc25025f 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -430,6 +430,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},
+ {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK},
};
static enum sof_ipc_process_type find_process(const char *name)
@@ -655,6 +656,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 +753,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 +769,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 +795,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 +832,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 +871,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 +930,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 +953,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)
{
@@ -1257,15 +1298,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);
@@ -2860,18 +2931,13 @@ 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 */
@@ -2885,34 +2951,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));
@@ -2920,44 +2972,44 @@ 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;
- }
+ /*
+ * 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;
/* send message to DSP */
- ret = sof_ipc_tx_message(sdev->ipc,
- ipc_config->hdr.cmd, ipc_config, size, &reply,
- sizeof(reply));
+ ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, size,
+ &reply, sizeof(reply));
if (ret < 0) {
dev_err(scomp->dev,
@@ -2967,14 +3019,13 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index,
}
/* 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;
}
@@ -3056,7 +3107,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) {
@@ -3065,6 +3116,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",
@@ -3087,13 +3141,26 @@ 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 snd_soc_tplg_private *private = &cfg->priv;
struct sof_ipc_reply reply;
u32 size = sizeof(*config);
int ret;
+ 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;
+ dev_dbg(scomp->dev, "ALH config rate %d channels %d\n",
+ config->alh.rate, config->alh.channels);
+
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
config->hdr.cmd, config, size, &reply,