Unverified Commit ec432e2a authored by Mark Brown's avatar Mark Brown
Browse files

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
parents 37a86b32 c8c960c1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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
+6 −1
Original line number Diff line number Diff line
# 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
+250 −0
Original line number Diff line number Diff line
// 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,
};
Loading