summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2022-05-17 18:19:35 +0100
committerMark Brown <broonie@kernel.org>2022-05-17 18:19:35 +0100
commitec432e2a51412bde3d2d9fa785d3812f55ccd696 (patch)
tree7a7aa98f38ceb78129d0a26bccd437f8161269ce
parent37a86b32bf0e5c5ca23567d7b120306b9ac8497d (diff)
parentc8c960c109716d96cfd1de65396fb8021eed4202 (diff)
downloadlinux-ec432e2a51412bde3d2d9fa785d3812f55ccd696.tar.gz
linux-ec432e2a51412bde3d2d9fa785d3812f55ccd696.tar.bz2
linux-ec432e2a51412bde3d2d9fa785d3812f55ccd696.zip
ASoC: Intel: avs: Driver core and PCM operations
Merge series from Cezary Rojewski <cezary.rojewski@intel.com>: Part three of main AVS driver series. This series was originally part of the initial series which was later divided [1] into smaller, easier-to-review chunks. Thus, many patches found here were already present on the list. This series consists of code typical to many drivers - PCI driver operations, trace ability, PM operations - as well as PCM handlers for all standard audio interfaces, that is, HDA, I2S and DMIC are found here. Series starts with updating firmware boot flow - libraries are no longer ignored. This change is dependent on already merged topology code [2] and because of that could not be part of the initial series [1]. PCM operations are split into four changes. First component operations alone i.e. operations which are usually agnostic towards path position (FE/BE). Then it continues with "generic" FE operations - there is no interface split here as from Intel ADSP point of view, FE, or HOST side as it's called in the specs, involves HD-Audio operations only. BE (also known as LINK) side on the other hand is divided into "non-HD-Audio" and HD-Audio part. The former represents transfer over DMIC and I2S interfaces both. While patches implementing standard PCI driver operations along (again standard) HD-Audio initialization routines followed up by power management handlers are two major ones, series covers also other important subjects such as: While patches implementing standard PCI driver operations along (again standard) HD-Audio initialization routines followed up by power management handlers are two major ones, series covers also other important subjects such as: - event tracing - preparation for firmware tracing (debugability) - coredump (debugability) - recovery flow (attempt recovery after IPC timeout or exception) - D0ix (D0 device substate, complements standard power management) Series is finalized by actual addition of supported platforms: SKL and APL-based. Platform-specific files are limited to firmware-specific bits, that is, bits that are specific to given firmware generation. Everything else is shared and is part of already upstream messaging code found in ipc.c, messages.c and messages.h files. Changes in v3: - addressed (hopefully) trace-code compilation under .configs with CONFIG_FTRACE dropped Changes in v2: - usage of avs_releast_last_firmware() dropped in error path for library loading procedure as suggested by Pierre - 'link_mask' usage replaced with 'i2s_link_mask' as requested by Pierre. Existing code addressed with new patch: "ASoC: Intel: avs: Replace link_mask usage with i2s_link_mask" - fixed possible race during recovery flow (->recovering flag is now atomic and tested in single location only). - dropped platform prefixes for basically all i2s board descriptors - 'ssp_test' renamed to 'i2s_test' to match naming convention of other boards - simplified PM implementation for current series, 'low_power' bits moved to future series - replaced SND_INTEL_DSP_DRIVER_SST with _AVS as suggested by Mark. Required changes to intel-dspcfg will be added in future series - number of typos across commit messages addressed [1]: https://lore.kernel.org/all/20220311153544.136854-1-cezary.rojewski@intel.com/ [2]: https://lore.kernel.org/all/20220331135246.993089-1-cezary.rojewski@intel.com/ Cezary Rojewski (14): ASoC: Intel: avs: Account for libraries when booting basefw ASoC: Intel: avs: Generic soc component driver ASoC: Intel: avs: Generic PCM FE operations ASoC: Intel: avs: non-HDA PCM BE operations ASoC: Intel: avs: HDA PCM BE operations ASoC: Intel: avs: Coredump and recovery flow ASoC: Intel: avs: Prepare for firmware tracing ASoC: Intel: avs: D0ix power state support ASoC: Intel: avs: Event tracing ASoC: Intel: avs: Machine board registration ASoC: Intel: avs: PCI driver implementation ASoC: Intel: avs: Power management ASoC: Intel: avs: SKL-based platforms support ASoC: Intel: avs: APL-based platforms support Piotr Maziarz (1): ASoC: Intel: avs: Replace link_mask usage with i2s_link_mask include/sound/intel-dsp-config.h | 3 +- include/sound/soc-acpi.h | 2 + sound/soc/intel/Kconfig | 2 + sound/soc/intel/avs/Makefile | 7 +- sound/soc/intel/avs/apl.c | 250 ++++++ sound/soc/intel/avs/avs.h | 79 ++ sound/soc/intel/avs/board_selection.c | 501 +++++++++++ sound/soc/intel/avs/core.c | 631 +++++++++++++ sound/soc/intel/avs/dsp.c | 27 +- sound/soc/intel/avs/ipc.c | 253 +++++- sound/soc/intel/avs/loader.c | 84 ++ sound/soc/intel/avs/messages.c | 35 +- sound/soc/intel/avs/messages.h | 51 ++ sound/soc/intel/avs/pcm.c | 1182 +++++++++++++++++++++++++ sound/soc/intel/avs/registers.h | 8 + sound/soc/intel/avs/skl.c | 125 +++ sound/soc/intel/avs/topology.c | 14 +- sound/soc/intel/avs/trace.c | 33 + sound/soc/intel/avs/trace.h | 154 ++++ sound/soc/intel/avs/utils.c | 23 + 20 files changed, 3443 insertions(+), 21 deletions(-) create mode 100644 sound/soc/intel/avs/apl.c create mode 100644 sound/soc/intel/avs/board_selection.c create mode 100644 sound/soc/intel/avs/pcm.c create mode 100644 sound/soc/intel/avs/skl.c create mode 100644 sound/soc/intel/avs/trace.c create mode 100644 sound/soc/intel/avs/trace.h -- 2.25.1
-rw-r--r--include/sound/intel-dsp-config.h3
-rw-r--r--include/sound/soc-acpi.h2
-rw-r--r--sound/soc/intel/Kconfig2
-rw-r--r--sound/soc/intel/avs/Makefile7
-rw-r--r--sound/soc/intel/avs/apl.c250
-rw-r--r--sound/soc/intel/avs/avs.h79
-rw-r--r--sound/soc/intel/avs/board_selection.c501
-rw-r--r--sound/soc/intel/avs/core.c631
-rw-r--r--sound/soc/intel/avs/dsp.c27
-rw-r--r--sound/soc/intel/avs/ipc.c253
-rw-r--r--sound/soc/intel/avs/loader.c84
-rw-r--r--sound/soc/intel/avs/messages.c35
-rw-r--r--sound/soc/intel/avs/messages.h51
-rw-r--r--sound/soc/intel/avs/pcm.c1182
-rw-r--r--sound/soc/intel/avs/registers.h8
-rw-r--r--sound/soc/intel/avs/skl.c125
-rw-r--r--sound/soc/intel/avs/topology.c14
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h154
-rw-r--r--sound/soc/intel/avs/utils.c23
20 files changed, 3443 insertions, 21 deletions
diff --git a/include/sound/intel-dsp-config.h b/include/sound/intel-dsp-config.h
index d4609077c258..34c975910574 100644
--- a/include/sound/intel-dsp-config.h
+++ b/include/sound/intel-dsp-config.h
@@ -15,7 +15,8 @@ enum {
SND_INTEL_DSP_DRIVER_LEGACY,
SND_INTEL_DSP_DRIVER_SST,
SND_INTEL_DSP_DRIVER_SOF,
- SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_SOF
+ SND_INTEL_DSP_DRIVER_AVS,
+ SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_AVS
};
#if IS_ENABLED(CONFIG_SND_INTEL_DSP_CONFIG)
diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h
index d33cf8df14b1..b38fd25c5729 100644
--- a/include/sound/soc-acpi.h
+++ b/include/sound/soc-acpi.h
@@ -156,6 +156,7 @@ struct snd_soc_acpi_link_adr {
* @links: array of link _ADR descriptors, null terminated.
* @drv_name: machine driver name
* @fw_filename: firmware file name. Used when SOF is not enabled.
+ * @tplg_filename: topology file name. Used when SOF is not enabled.
* @board: board name
* @machine_quirk: pointer to quirk, usually based on DMI information when
* ACPI ID alone is not sufficient, wrong or misleading
@@ -174,6 +175,7 @@ struct snd_soc_acpi_mach {
const struct snd_soc_acpi_link_adr *links;
const char *drv_name;
const char *fw_filename;
+ const char *tplg_filename;
const char *board;
struct snd_soc_acpi_mach * (*machine_quirk)(void *arg);
const void *quirk_data;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 039b45a4a799..7c85d1bb9c12 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -216,9 +216,11 @@ config SND_SOC_INTEL_AVS
depends on COMMON_CLK
select SND_SOC_ACPI if ACPI
select SND_SOC_TOPOLOGY
+ select SND_HDA
select SND_HDA_EXT_CORE
select SND_HDA_DSP_LOADER
select SND_INTEL_DSP_CONFIG
+ select WANT_DEV_COREDUMP
help
Enable support for Intel(R) cAVS 1.5 platforms with DSP
capabilities. This includes Skylake, Kabylake, Amberlake and
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 952f51977656..b6b93ae80304 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,7 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
- topology.o path.o
+ topology.o path.o pcm.o board_selection.o
snd-soc-avs-objs += cldma.o
+snd-soc-avs-objs += skl.o apl.o
+
+snd-soc-avs-objs += trace.o
+# tell define_trace.h where to find the trace header
+CFLAGS_trace.o := -I$(src)
obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c
new file mode 100644
index 000000000000..b8e2b23c9f64
--- /dev/null
+++ b/sound/soc/intel/avs/apl.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/devcoredump.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+#include "path.h"
+#include "topology.h"
+
+static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+ u32 fifo_full_period, unsigned long resource_mask, u32 *priorities)
+{
+ struct apl_log_state_info *info;
+ u32 size, num_cores = adev->hw_cfg.dsp_cores;
+ int ret, i;
+
+ if (fls_long(resource_mask) > num_cores)
+ return -EINVAL;
+ size = struct_size(info, logs_core, num_cores);
+ info = kzalloc(size, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->aging_timer_period = aging_period;
+ info->fifo_full_timer_period = fifo_full_period;
+ info->core_mask = resource_mask;
+ if (enable)
+ for_each_set_bit(i, &resource_mask, num_cores) {
+ info->logs_core[i].enable = enable;
+ info->logs_core[i].min_priority = *priorities++;
+ }
+ else
+ for_each_set_bit(i, &resource_mask, num_cores)
+ info->logs_core[i].enable = enable;
+
+ ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size);
+ kfree(info);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
+static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct apl_log_buffer_layout layout;
+ unsigned long flags;
+ void __iomem *addr, *buf;
+
+ addr = avs_log_buffer_addr(adev, msg->log.core);
+ if (!addr)
+ return -ENXIO;
+
+ memcpy_fromio(&layout, addr, sizeof(layout));
+
+ spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+ if (!kfifo_initialized(&adev->dbg.trace_fifo))
+ /* consume the logs regardless of consumer presence */
+ goto update_read_ptr;
+
+ buf = apl_log_payload_addr(addr);
+
+ if (layout.read_ptr > layout.write_ptr) {
+ __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+ apl_log_payload_size(adev) - layout.read_ptr,
+ &adev->dbg.fifo_lock);
+ layout.read_ptr = 0;
+ }
+ __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr,
+ layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock);
+
+ wake_up(&adev->dbg.trace_waitq);
+
+update_read_ptr:
+ spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+ writel(layout.write_ptr, addr);
+ return 0;
+}
+
+static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout)
+{
+ unsigned long timeout;
+ void __iomem *addr;
+
+ addr = avs_log_buffer_addr(adev, core);
+ if (!addr)
+ return -ENXIO;
+
+ timeout = jiffies + msecs_to_jiffies(10);
+
+ do {
+ memcpy_fromio(layout, addr, sizeof(*layout));
+ if (layout->read_ptr != layout->write_ptr)
+ return 0;
+ usleep_range(500, 1000);
+ } while (!time_after(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+/* reads log header and tests its type */
+#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1)
+
+static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+ struct apl_log_buffer_layout layout;
+ void __iomem *addr, *buf;
+ size_t dump_size;
+ u16 offset = 0;
+ u8 *dump, *pos;
+
+ dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size;
+ dump = vzalloc(dump_size);
+ if (!dump)
+ return -ENOMEM;
+
+ memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+
+ if (!msg->ext.coredump.stack_dump_size)
+ goto exit;
+
+ /* Dump the registers even if an external error prevents gathering the stack. */
+ addr = avs_log_buffer_addr(adev, msg->ext.coredump.core_id);
+ if (!addr)
+ goto exit;
+
+ buf = apl_log_payload_addr(addr);
+ memcpy_fromio(&layout, addr, sizeof(layout));
+ if (!apl_is_entry_stackdump(buf + layout.read_ptr)) {
+ /*
+ * DSP awaits the remaining logs to be
+ * gathered before dumping stack
+ */
+ msg->log.core = msg->ext.coredump.core_id;
+ avs_dsp_op(adev, log_buffer_status, msg);
+ }
+
+ pos = dump + AVS_FW_REGS_SIZE;
+ /* gather the stack */
+ do {
+ u32 count;
+
+ if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout))
+ break;
+
+ if (layout.read_ptr > layout.write_ptr) {
+ count = apl_log_payload_size(adev) - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ layout.read_ptr = 0;
+ offset += count;
+ }
+ count = layout.write_ptr - layout.read_ptr;
+ memcpy_fromio(pos + offset, buf + layout.read_ptr, count);
+ offset += count;
+
+ /* update read pointer */
+ writel(layout.write_ptr, addr);
+ } while (offset < msg->ext.coredump.stack_dump_size);
+
+exit:
+ dev_coredumpv(adev->dev, dump, dump_size, GFP_KERNEL);
+
+ return 0;
+}
+
+static bool apl_lp_streaming(struct avs_dev *adev)
+{
+ struct avs_path *path;
+
+ /* Any gateway without buffer allocated in LP area disqualifies D0IX. */
+ list_for_each_entry(path, &adev->path_list, node) {
+ struct avs_path_pipeline *ppl;
+
+ list_for_each_entry(ppl, &path->ppl_list, node) {
+ struct avs_path_module *mod;
+
+ list_for_each_entry(mod, &ppl->mod_list, node) {
+ struct avs_tplg_modcfg_ext *cfg;
+
+ cfg = mod->template->cfg_ext;
+
+ /* only copiers have gateway attributes */
+ if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID))
+ continue;
+ /* non-gateway copiers do not prevent PG */
+ if (cfg->copier.dma_type == INVALID_OBJECT_ID)
+ continue;
+
+ if (!mod->gtw_attrs.lp_buffer_alloc)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+ /* wake in all cases */
+ if (wake)
+ return true;
+
+ /*
+ * If no pipelines are running, allow for d0ix schedule.
+ * If all gateways have lp=1, allow for d0ix schedule.
+ * If any gateway with lp=0 is allocated, abort scheduling d0ix.
+ *
+ * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition,
+ * not the power-gating mechanism known from cAVS 2.0.
+ */
+ return apl_lp_streaming(adev);
+}
+
+static int apl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+ bool streaming = false;
+ int ret;
+
+ if (enable)
+ /* Either idle or all gateways with lp=1. */
+ streaming = !list_empty(&adev->path_list);
+
+ ret = avs_ipc_set_d0ix(adev, enable, streaming);
+ return AVS_IPC_RET(ret);
+}
+
+const struct avs_dsp_ops apl_dsp_ops = {
+ .power = avs_dsp_core_power,
+ .reset = avs_dsp_core_reset,
+ .stall = avs_dsp_core_stall,
+ .irq_handler = avs_dsp_irq_handler,
+ .irq_thread = avs_dsp_irq_thread,
+ .int_control = avs_dsp_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .enable_logs = apl_enable_logs,
+ .log_buffer_offset = skl_log_buffer_offset,
+ .log_buffer_status = apl_log_buffer_status,
+ .coredump = apl_coredump,
+ .d0ix_toggle = apl_d0ix_toggle,
+ .set_d0ix = apl_set_d0ix,
+};
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index c57a07a18d8e..92e37722d280 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/kfifo.h>
#include <sound/hda_codec.h>
#include <sound/hda_register.h>
#include <sound/soc-component.h>
@@ -19,6 +20,9 @@
struct avs_dev;
struct avs_tplg;
+struct avs_tplg_library;
+struct avs_soc_component;
+struct avs_ipc_msg;
/*
* struct avs_dsp_ops - Platform-specific DSP operations
@@ -40,11 +44,21 @@ struct avs_dsp_ops {
int (* const load_basefw)(struct avs_dev *, struct firmware *);
int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+ int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
+ u32 *);
+ int (* const log_buffer_offset)(struct avs_dev *, u32);
+ int (* const log_buffer_status)(struct avs_dev *, union avs_notify_msg *);
+ int (* const coredump)(struct avs_dev *, union avs_notify_msg *);
+ bool (* const d0ix_toggle)(struct avs_dev *, struct avs_ipc_msg *, bool);
+ int (* const set_d0ix)(struct avs_dev *, bool);
};
#define avs_dsp_op(adev, op, ...) \
((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
+extern const struct avs_dsp_ops skl_dsp_ops;
+extern const struct avs_dsp_ops apl_dsp_ops;
+
#define AVS_PLATATTR_CLDMA BIT_ULL(0)
#define AVS_PLATATTR_IMR BIT_ULL(1)
@@ -72,6 +86,16 @@ struct avs_fw_entry {
struct list_head node;
};
+struct avs_debug {
+ struct kfifo trace_fifo;
+ spinlock_t fifo_lock; /* serialize I/O for trace_fifo */
+ spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */
+ wait_queue_head_t trace_waitq;
+ u32 aging_timer_period;
+ u32 fifo_full_timer_period;
+ u32 logged_resources; /* context dependent: core or library */
+};
+
/*
* struct avs_dev - Intel HD-Audio driver data
*
@@ -105,6 +129,7 @@ struct avs_dev {
char **lib_names;
struct completion fw_ready;
+ struct work_struct probe_work;
struct nhlt_acpi_table *nhlt;
struct list_head comp_list;
@@ -112,6 +137,8 @@ struct avs_dev {
struct list_head path_list;
spinlock_t path_list_lock;
struct mutex path_mutex;
+
+ struct avs_debug dbg;
};
/* from hda_bus to avs_dev */
@@ -162,12 +189,18 @@ struct avs_ipc {
struct avs_ipc_msg rx;
u32 default_timeout_ms;
bool ready;
+ atomic_t recovering;
bool rx_completed;
spinlock_t rx_lock;
struct mutex msg_mutex;
struct completion done_completion;
struct completion busy_completion;
+
+ struct work_struct recovery_work;
+ struct delayed_work d0ix_work;
+ atomic_t d0ix_disable_depth;
+ bool in_d0ix;
};
#define AVS_EIPC EREMOTEIO
@@ -204,6 +237,11 @@ int avs_dsp_send_msg_timeout(struct avs_dev *adev,
struct avs_ipc_msg *reply, int timeout);
int avs_dsp_send_msg(struct avs_dev *adev,
struct avs_ipc_msg *request, struct avs_ipc_msg *reply);
+/* Two variants below are for messages that control DSP power states. */
+int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, int timeout, bool wake_d0i0);
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+ struct avs_ipc_msg *reply, bool wake_d0i0);
int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev,
struct avs_ipc_msg *request, int timeout);
int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request);
@@ -211,6 +249,11 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable);
int avs_ipc_init(struct avs_ipc *ipc, struct device *dev);
void avs_ipc_block(struct avs_ipc *ipc);
+int avs_dsp_disable_d0ix(struct avs_dev *adev);
+int avs_dsp_enable_d0ix(struct avs_dev *adev);
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core);
+
/* Firmware resources management */
int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
@@ -241,6 +284,7 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable);
void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable);
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs);
int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
int avs_dsp_first_boot_firmware(struct avs_dev *adev);
@@ -267,4 +311,39 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name);
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+ unsigned long *tdms);
+int avs_hda_platform_register(struct avs_dev *adev, const char *name);
+
+int avs_register_all_boards(struct avs_dev *adev);
+void avs_unregister_all_boards(struct avs_dev *adev);
+
+/* Firmware tracing helpers */
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+ spinlock_t *lock);
+
+#define avs_log_buffer_size(adev) \
+ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores)
+
+#define avs_log_buffer_addr(adev, core) \
+({ \
+ s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \
+ (__offset < 0) ? NULL : \
+ (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \
+})
+
+struct apl_log_buffer_layout {
+ u32 read_ptr;
+ u32 write_ptr;
+ u8 buffer[];
+} __packed;
+
+#define apl_log_payload_size(adev) \
+ (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout))
+
+#define apl_log_payload_addr(addr) \
+ (addr + sizeof(struct apl_log_buffer_layout))
+
#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644
index 000000000000..80cb0164678a
--- /dev/null
+++ b/sound/soc/intel/avs/board_selection.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+
+static bool i2s_test;
+module_param(i2s_test, bool, 0444);
+MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards");
+
+static const struct dmi_system_id kbl_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"),
+ },
+ },
+ {}
+};
+
+static const struct dmi_system_id kblr_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"),
+ },
+ },
+ {}
+};
+
+static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg)
+{
+ struct snd_soc_acpi_mach *mach = arg;
+ const struct dmi_system_id *dmi_id;
+ struct dmi_system_id *dmi_table;
+
+ if (mach->quirk_data == NULL)
+ return mach;
+
+ dmi_table = (struct dmi_system_id *)mach->quirk_data;
+
+ dmi_id = dmi_first_match(dmi_table);
+ if (!dmi_id)
+ return NULL;
+
+ return mach;
+}
+
+#define AVS_SSP(x) (BIT(x))
+#define AVS_SSP_RANGE(a, b) (GENMASK(b, a))
+
+/* supported I2S board codec configurations */
+static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "avs_nau8825",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "nau8825-tplg.bin",
+ },
+ {
+ .id = "INT343B",
+ .drv_name = "avs_ssm4567",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "ssm4567-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt286",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kbl_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt286-tplg.bin",
+ },
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .quirk_data = &kblr_dmi_table,
+ .machine_quirk = dmi_match_quirk,
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "MX98373",
+ .drv_name = "avs_max98373",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "max98373-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {
+ .id = "INT34C3",
+ .drv_name = "avs_tdf8532",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP_RANGE(0, 5),
+ },
+ .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+ .tplg_filename = "tdf8532-tplg.bin",
+ },
+ {
+ .id = "MX98357A",
+ .drv_name = "avs_max98357a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "max98357a-tplg.bin",
+ },
+ {
+ .id = "DLGS7219",
+ .drv_name = "avs_da7219",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "da7219-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+ {
+ .id = "INT343A",
+ .drv_name = "avs_rt298",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "rt298-tplg.bin",
+ },
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(1),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(2),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(3),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(4),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ {
+ .drv_name = "avs_i2s_test",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(5),
+ },
+ .tplg_filename = "i2s-test-tplg.bin",
+ },
+ /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+};
+
+struct avs_acpi_boards {
+ int id;
+ struct snd_soc_acpi_mach *machs;
+};
+
+#define AVS_MACH_ENTRY(_id, _mach) \
+ { .id = (_id), .machs = (_mach), }
+
+/* supported I2S boards per platform */
+static const struct avs_acpi_boards i2s_boards[] = {
+ AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */
+ AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */
+ AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */
+ AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */
+ {},
+};
+
+static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+{
+ int id, i;
+
+ id = adev->base.pci->device;
+ for (i = 0; i < ARRAY_SIZE(i2s_boards); i++)
+ if (i2s_boards[i].id == id)
+ return &i2s_boards[i];
+ return NULL;
+}
+
+/* platform devices owned by AVS audio are removed with this hook */
+static void board_pdev_unregister(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int avs_register_dmic_board(struct avs_dev *adev)
+{
+ struct platform_device *codec, *board;
+ struct snd_soc_acpi_mach mach = {{0}};
+ int ret;
+
+ if (!adev->nhlt ||
+ !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) {
+ dev_dbg(adev->dev, "no DMIC endpoints present\n");
+ return 0;
+ }
+
+ codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(codec)) {
+ dev_err(adev->dev, "dmic codec register failed\n");
+ return PTR_ERR(codec);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, codec);
+ if (ret < 0) {
+ platform_device_unregister(codec);
+ return ret;
+ }
+
+ ret = avs_dmic_platform_register(adev, "dmic-platform");
+ if (ret < 0)
+ return ret;
+
+ mach.tplg_filename = "dmic-tplg.bin";
+ mach.mach_params.platform = "dmic-platform";
+
+ board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE,
+ (const void *)&mach, sizeof(mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "dmic board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, board);
+ if (ret < 0) {
+ platform_device_unregister(board);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
+{
+ struct platform_device *board;
+ int num_ssps;
+ char *name;
+ int ret;
+
+ num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
+ dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n",
+ num_ssps, mach->drv_name, __fls(mach->mach_params.i2s_link_mask));
+ return -ENODEV;
+ }
+
+ name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
+ mach->mach_params.i2s_link_mask);
+ if (!name)
+ return -ENOMEM;
+
+ ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
+ if (ret < 0)
+ return ret;
+
+ mach->mach_params.platform = name;
+
+ board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask,
+ (const void *)mach, sizeof(*mach));
+ if (IS_ERR(board)) {
+ dev_err(adev->dev, "ssp board register failed\n");
+ return PTR_ERR(board);
+ }
+
+ ret = devm_add_action(adev->dev, board_pdev_unregister, bo