All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations
@ 2022-04-26 17:23 Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw Cezary Rojewski
                   ` (14 more replies)
  0 siblings, 15 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

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.


[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

 include/sound/soc-acpi.h              |    2 +
 sound/soc/intel/Kconfig               |    4 +-
 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 |  463 ++++++++++
 sound/soc/intel/avs/core.c            |  655 ++++++++++++++
 sound/soc/intel/avs/dsp.c             |   27 +-
 sound/soc/intel/avs/ipc.c             |  249 +++++-
 sound/soc/intel/avs/loader.c          |   83 ++
 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        |    2 -
 sound/soc/intel/avs/trace.c           |   33 +
 sound/soc/intel/avs/trace.h           |  158 ++++
 sound/soc/intel/avs/utils.c           |   23 +
 19 files changed, 3421 insertions(+), 15 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


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:21   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver Cezary Rojewski
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Not all modules are part of base firmware. Some are part of loadable
libraries. These need to be loaded after base firmware reports ready
status through FW_READY notification.

Their loading process is similar to the base firmware's one. Request the
binary file, verify and strip the manifest and load the actual code into
DSP memory with help of CLDMA or HD-Audio render stream, depending on
audio device generation.

List of libraries needed for loading is obtained through the topology -
vendor sections specifying the name of firmware files to request.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/avs.h    |  3 ++
 sound/soc/intel/avs/loader.c | 79 ++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index c57a07a18d8e..14b4a780a91c 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -19,6 +19,8 @@
 
 struct avs_dev;
 struct avs_tplg;
+struct avs_tplg_library;
+struct avs_soc_component;
 
 /*
  * struct avs_dsp_ops - Platform-specific DSP operations
@@ -241,6 +243,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);
 
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index c47f85161d95..de98f4c3adf8 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -15,6 +15,7 @@
 #include "cldma.h"
 #include "messages.h"
 #include "registers.h"
+#include "topology.h"
 
 #define AVS_ROM_STS_MASK		0xFF
 #define AVS_ROM_INIT_DONE		0x1
@@ -466,6 +467,70 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
 	return 0;
 }
 
+int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
+{
+	int start, id, i = 0;
+	int ret;
+
+	/* Calculate the id to assign for the next lib. */
+	for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
+		if (adev->lib_names[id][0] == '\0')
+			break;
+	if (id + num_libs >= adev->fw_cfg.max_libs_count)
+		return -EINVAL;
+
+	start = id;
+	while (i < num_libs) {
+		struct avs_fw_manifest *man;
+		const struct firmware *fw;
+		struct firmware stripped_fw;
+		char *filename;
+		int j;
+
+		filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
+				     libs[i].name);
+		if (!filename)
+			return -ENOMEM;
+
+		ret = avs_request_firmware(adev, &fw, filename);
+		kfree(filename);
+		if (ret < 0)
+			return ret;
+
+		stripped_fw = *fw;
+		ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
+		if (ret) {
+			dev_err(adev->dev, "invalid library data: %d\n", ret);
+			goto release_fw;
+		}
+
+		ret = avs_fw_manifest_offset(&stripped_fw);
+		if (ret < 0)
+			goto release_fw;
+		man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
+
+		/* Don't load anything that's already in DSP memory. */
+		for (j = 0; j < id; j++)
+			if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
+				goto next_lib;
+
+		ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
+		if (ret)
+			goto release_fw;
+
+		strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
+		id++;
+next_lib:
+		i++;
+	}
+
+	return start == id ? 1 : 0;
+
+release_fw:
+	avs_release_last_firmware(adev);
+	return ret;
+}
+
 static int avs_dsp_load_basefw(struct avs_dev *adev)
 {
 	const struct avs_fw_version *min_req;
@@ -519,6 +584,7 @@ static int avs_dsp_load_basefw(struct avs_dev *adev)
 
 int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
 {
+	struct avs_soc_component *acomp;
 	int ret, i;
 
 	/* Forgo full boot if flash from IMR succeeds. */
@@ -538,7 +604,20 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
 	avs_hda_l1sen_enable(adev, false);
 
 	ret = avs_dsp_load_basefw(adev);
+	if (ret)
+		goto reenable_gating;
+
+	mutex_lock(&adev->comp_list_mutex);
+	list_for_each_entry(acomp, &adev->comp_list, node) {
+		struct avs_tplg *tplg = acomp->tplg;
+
+		ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+		if (ret < 0)
+			break;
+	}
+	mutex_unlock(&adev->comp_list_mutex);
 
+reenable_gating:
 	avs_hda_l1sen_enable(adev, true);
 	avs_hda_clock_gating_enable(adev, true);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:33   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 03/14] ASoC: Intel: avs: Generic PCM FE operations Cezary Rojewski
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Prepare for concrete PCM operations over HDA, DMIC and I2S interfaces by
providing generic soc component implementation. Interface-specific
components re-use this code as majority of flow is shared.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 include/sound/soc-acpi.h     |   2 +
 sound/soc/intel/avs/Makefile |   2 +-
 sound/soc/intel/avs/pcm.c    | 277 +++++++++++++++++++++++++++++++++++
 3 files changed, 280 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/pcm.c

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/avs/Makefile b/sound/soc/intel/avs/Makefile
index 952f51977656..62b3581d6cdb 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,7 +1,7 @@
 # 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
 snd-soc-avs-objs += cldma.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
new file mode 100644
index 000000000000..29a02f058540
--- /dev/null
+++ b/sound/soc/intel/avs/pcm.c
@@ -0,0 +1,277 @@
+// 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/debugfs.h>
+#include <linux/device.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-component.h>
+#include "avs.h"
+#include "path.h"
+#include "topology.h"
+
+struct avs_dma_data {
+	struct avs_tplg_path_template *template;
+	struct avs_path *path;
+	/*
+	 * link stream is stored within substream's runtime
+	 * private_data to fulfill the needs of codec BE path
+	 *
+	 * host stream assigned
+	 */
+	struct hdac_ext_stream *host_stream;
+};
+
+static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
+				  loff_t *ppos)
+{
+	struct snd_soc_component *component = file->private_data;
+	struct snd_soc_card *card = component->card;
+	struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+	char buf[64];
+	size_t len;
+
+	len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
+		       mach->tplg_filename);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations topology_name_fops = {
+	.open = simple_open,
+	.read = topology_name_read,
+	.llseek = default_llseek,
+};
+
+static int avs_component_load_libraries(struct avs_soc_component *acomp)
+{
+	struct avs_tplg *tplg = acomp->tplg;
+	struct avs_dev *adev = to_avs_dev(acomp->base.dev);
+	int ret;
+
+	if (!tplg->num_libs)
+		return 0;
+
+	/* Parent device may be asleep and library loading involves IPCs. */
+	ret = pm_runtime_get_sync(adev->dev);
+	if (ret < 0 && ret != -EACCES) {
+		pm_runtime_put_noidle(adev->dev);
+		return ret;
+	}
+
+	avs_hda_clock_gating_enable(adev, false);
+	avs_hda_l1sen_enable(adev, false);
+
+	ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
+
+	avs_hda_l1sen_enable(adev, true);
+	avs_hda_clock_gating_enable(adev, true);
+
+	if (!ret)
+		ret = avs_module_info_init(adev, false);
+
+	pm_runtime_mark_last_busy(adev->dev);
+	pm_runtime_put_autosuspend(adev->dev);
+
+	return ret;
+}
+
+static int avs_component_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_card *card = component->card;
+	struct snd_soc_acpi_mach *mach;
+	struct avs_soc_component *acomp;
+	struct avs_dev *adev;
+	char *filename;
+	int ret;
+
+	dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
+	mach = dev_get_platdata(card->dev);
+	acomp = to_avs_soc_component(component);
+	adev = to_avs_dev(component->dev);
+
+	acomp->tplg = avs_tplg_new(component);
+	if (!acomp->tplg)
+		return -ENOMEM;
+
+	if (!mach->tplg_filename)
+		goto finalize;
+
+	/* Load specified topology and create sysfs for it. */
+	filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
+			     mach->tplg_filename);
+	if (!filename)
+		return -ENOMEM;
+
+	ret = avs_load_topology(component, filename);
+	kfree(filename);
+	if (ret < 0)
+		return ret;
+
+	ret = avs_component_load_libraries(acomp);
+	if (ret < 0) {
+		dev_err(card->dev, "libraries loading failed: %d\n", ret);
+		goto err_load_libs;
+	}
+
+finalize:
+	debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
+			    &topology_name_fops);
+
+	mutex_lock(&adev->comp_list_mutex);
+	list_add_tail(&acomp->node, &adev->comp_list);
+	mutex_unlock(&adev->comp_list_mutex);
+
+	return 0;
+
+err_load_libs:
+	avs_remove_topology(component);
+	return ret;
+}
+
+static void avs_component_remove(struct snd_soc_component *component)
+{
+	struct avs_soc_component *acomp = to_avs_soc_component(component);
+	struct snd_soc_acpi_mach *mach;
+	struct avs_dev *adev = to_avs_dev(component->dev);
+	int ret;
+
+	mach = dev_get_platdata(component->card->dev);
+
+	mutex_lock(&adev->comp_list_mutex);
+	list_del(&acomp->node);
+	mutex_unlock(&adev->comp_list_mutex);
+
+	if (mach->tplg_filename) {
+		ret = avs_remove_topology(component);
+		if (ret < 0)
+			dev_err(component->dev, "unload topology failed: %d\n", ret);
+	}
+}
+
+static int avs_component_open(struct snd_soc_component *component,
+			      struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_pcm_hardware hwparams;
+
+	/* only FE DAI links are handled here */
+	if (rtd->dai_link->no_pcm)
+		return 0;
+
+	hwparams.info = SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_MMAP_VALID |
+			SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_PAUSE |
+			SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+
+	hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S32_LE;
+	hwparams.period_bytes_min = 128;
+	hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2;
+	hwparams.periods_min = 2;
+	hwparams.periods_max = AZX_MAX_FRAG;
+	hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE;
+	hwparams.fifo_size = 0;
+
+	return snd_soc_set_runtime_hwparams(substream, &hwparams);
+}
+
+static unsigned int avs_hstream_dpib_read(struct hdac_stream *hstream)
+{
+	return readl(hstream->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
+		     (AZX_REG_VS_SDXDPIB_XINTERVAL * hstream->index));
+}
+
+static snd_pcm_uframes_t
+avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct avs_dma_data *data;
+	struct hdac_stream *hstream;
+	unsigned int pos;
+
+	data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+	if (!data->host_stream)
+		return 0;
+
+	hstream = hdac_stream(data->host_stream);
+	pos = avs_hstream_dpib_read(hstream);
+
+	if (pos >= hstream->bufsize)
+		pos = 0;
+
+	return bytes_to_frames(substream->runtime, pos);
+}
+
+static int avs_component_mmap(struct snd_soc_component *component,
+			      struct snd_pcm_substream *substream,
+			      struct vm_area_struct *vma)
+{
+	return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+#define MAX_PREALLOC_SIZE	(32 * 1024 * 1024)
+
+static int avs_component_construct(struct snd_soc_component *component,
+				   struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+	struct snd_pcm *pcm = rtd->pcm;
+
+	if (dai->driver->playback.channels_min)
+		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+					   SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+					   MAX_PREALLOC_SIZE);
+
+	if (dai->driver->capture.channels_min)
+		snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+					   SNDRV_DMA_TYPE_DEV_SG, component->dev, 0,
+					   MAX_PREALLOC_SIZE);
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver avs_component_driver = {
+	.name			= "avs-pcm",
+	.probe			= avs_component_probe,
+	.remove			= avs_component_remove,
+	.open			= avs_component_open,
+	.pointer		= avs_component_pointer,
+	.mmap			= avs_component_mmap,
+	.pcm_construct		= avs_component_construct,
+	.module_get_upon_open	= 1, /* increment refcount when a pcm is opened */
+	.topology_name_prefix	= "intel/avs",
+	.non_legacy_dai_naming	= true,
+};
+
+__maybe_unused
+static int avs_soc_component_register(struct device *dev, const char *name,
+				      const struct snd_soc_component_driver *drv,
+				      struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
+{
+	struct avs_soc_component *acomp;
+	int ret;
+
+	acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL);
+	if (!acomp)
+		return -ENOMEM;
+
+	ret = snd_soc_component_initialize(&acomp->base, drv, dev);
+	if (ret < 0)
+		return ret;
+
+	/* force name change after ASoC is done with its init */
+	acomp->base.name = name;
+	INIT_LIST_HEAD(&acomp->node);
+
+	return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 03/14] ASoC: Intel: avs: Generic PCM FE operations
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations Cezary Rojewski
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Each stream in AVS is represented by FE and BE domain. FE path stands
for HOST part of the stream while BE stands for LINK (hardware) one.
While BE portion is interface specific, FE is not. Handle all standard
DAI operations to implement FE part of the stream.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/pcm.c      | 336 +++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/topology.c |   2 -
 2 files changed, 336 insertions(+), 2 deletions(-)

diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 29a02f058540..725caab7cf28 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -10,6 +10,7 @@
 #include <linux/device.h>
 #include <sound/hda_register.h>
 #include <sound/hdaudio_ext.h>
+#include <sound/pcm_params.h>
 #include <sound/soc-acpi.h>
 #include <sound/soc-acpi-intel-match.h>
 #include <sound/soc-component.h>
@@ -29,6 +30,341 @@ struct avs_dma_data {
 	struct hdac_ext_stream *host_stream;
 };
 
+static struct avs_tplg_path_template *
+avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
+{
+	struct snd_soc_dapm_widget *dw;
+	struct snd_soc_dapm_path *dp;
+	enum snd_soc_dapm_direction dir;
+
+	if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+		dw = dai->capture_widget;
+		dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN;
+	} else {
+		dw = dai->playback_widget;
+		dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT;
+	}
+
+	dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]);
+	if (!dp)
+		return NULL;
+
+	/* Get the other widget, with actual path template data */
+	dw = (dp->source == dw) ? dp->sink : dp->source;
+
+	return dw->priv;
+}
+
+static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe)
+{
+	struct avs_tplg_path_template *template;
+	struct avs_dma_data *data;
+
+	template = avs_dai_find_path_template(dai, is_fe, substream->stream);
+	if (!template) {
+		dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n",
+			snd_pcm_stream_str(substream), dai->name);
+		return -EINVAL;
+	}
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->template = template;
+	snd_soc_dai_set_dma_data(dai, substream, data);
+
+	return 0;
+}
+
+static int avs_dai_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *fe_hw_params,
+			     struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+			     int dma_id)
+{
+	struct avs_dma_data *data;
+	struct avs_path *path;
+	struct avs_dev *adev = to_avs_dev(dai->dev);
+	int ret;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+
+	dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p",
+		__func__, substream, substream->runtime);
+	dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+		params_rate(fe_hw_params), params_channels(fe_hw_params),
+		params_width(fe_hw_params), params_physical_width(fe_hw_params));
+
+	dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p",
+		__func__, substream, substream->runtime);
+	dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n",
+		params_rate(be_hw_params), params_channels(be_hw_params),
+		params_width(be_hw_params), params_physical_width(be_hw_params));
+
+	path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params);
+	if (IS_ERR(path)) {
+		ret = PTR_ERR(path);
+		dev_err(dai->dev, "create path failed: %d\n", ret);
+		return ret;
+	}
+
+	data->path = path;
+	return 0;
+}
+
+static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	int ret;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (!data->path)
+		return 0;
+
+	ret = avs_path_reset(data->path);
+	if (ret < 0) {
+		dev_err(dai->dev, "reset path failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = avs_path_pause(data->path);
+	if (ret < 0)
+		dev_err(dai->dev, "pause path failed: %d\n", ret);
+	return ret;
+}
+
+static const unsigned int rates[] = {
+	8000, 11025, 12000, 16000,
+	22050, 24000, 32000, 44100,
+	48000, 64000, 88200, 96000,
+	128000, 176400, 192000,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+	.mask = 0,
+};
+
+static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct avs_dma_data *data;
+	struct avs_dev *adev = to_avs_dev(dai->dev);
+	struct hdac_bus *bus = &adev->base.core;
+	struct hdac_ext_stream *estream;
+	int ret;
+
+	ret = avs_dai_startup(substream, dai, true);
+	if (ret)
+		return ret;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+
+	estream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST);
+	if (!estream) {
+		kfree(data);
+		return -EBUSY;
+	}
+
+	data->host_stream = estream;
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+	/* avoid wrap-around with wall-clock */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates);
+	snd_pcm_set_sync(substream);
+
+	dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p",
+		__func__, hdac_stream(estream)->stream_tag, substream);
+
+	return 0;
+}
+
+static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST);
+	kfree(data);
+}
+
+static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+	struct snd_pcm_hw_params *be_hw_params = NULL;
+	struct snd_soc_pcm_runtime *fe, *be;
+	struct snd_soc_dpcm *dpcm;
+	struct avs_dma_data *data;
+	struct hdac_stream *hstream;
+	int ret;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (data->path)
+		return 0;
+
+	hstream = hdac_stream(data->host_stream);
+	hstream->bufsize = 0;
+	hstream->period_bytes = 0;
+	hstream->format_val = 0;
+
+	fe = asoc_substream_to_rtd(substream);
+	for_each_dpcm_be(fe, substream->stream, dpcm) {
+		be = dpcm->be;
+		be_hw_params = &be->dpcm[substream->stream].hw_params;
+	}
+
+	ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai, hstream->stream_tag - 1);
+	if (ret)
+		goto create_err;
+
+	ret = avs_path_bind(data->path);
+	if (ret < 0) {
+		dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret);
+		goto bind_err;
+	}
+
+	return 0;
+
+bind_err:
+	avs_path_free(data->path);
+	data->path = NULL;
+create_err:
+	snd_pcm_lib_free_pages(substream);
+	return ret;
+}
+
+static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	struct hdac_stream *hstream;
+	int ret;
+
+	dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p",
+		__func__, substream, substream->runtime);
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (!data->path)
+		return 0;
+
+	hstream = hdac_stream(data->host_stream);
+
+	ret = avs_path_unbind(data->path);
+	if (ret < 0)
+		dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret);
+
+	avs_path_free(data->path);
+	data->path = NULL;
+	snd_hdac_stream_cleanup(hstream);
+	hstream->prepared = false;
+
+	ret = snd_pcm_lib_free_pages(substream);
+	if (ret < 0)
+		dev_dbg(dai->dev, "Failed to free pages!\n");
+
+	return ret;
+}
+
+static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct avs_dma_data *data;
+	struct avs_dev *adev = to_avs_dev(dai->dev);
+	struct hdac_stream *hstream;
+	struct hdac_bus *bus;
+	unsigned int format_val;
+	int ret;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	hstream = hdac_stream(data->host_stream);
+
+	if (hstream->prepared)
+		return 0;
+
+	bus = hstream->bus;
+	snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
+	snd_hdac_stream_reset(hstream);
+
+	format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+						 runtime->sample_bits, 0);
+
+	ret = snd_hdac_stream_set_params(hstream, format_val);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_hdac_stream_setup(hstream);
+	if (ret < 0)
+		return ret;
+
+	ret = avs_dai_prepare(adev, substream, dai);
+	if (ret)
+		return ret;
+
+	hstream->prepared = true;
+	return 0;
+}
+
+static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	struct hdac_stream *hstream;
+	struct hdac_bus *bus;
+	unsigned long flags;
+	int ret = 0;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	hstream = hdac_stream(data->host_stream);
+	bus = hstream->bus;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock_irqsave(&bus->reg_lock, flags);
+		snd_hdac_stream_start(hstream, true);
+		spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+		if (ret < 0)
+			dev_err(dai->dev, "run FE path failed: %d\n", ret);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		ret = avs_path_pause(data->path);
+		if (ret < 0)
+			dev_err(dai->dev, "pause FE path failed: %d\n", ret);
+
+		spin_lock_irqsave(&bus->reg_lock, flags);
+		snd_hdac_stream_stop(hstream);
+		spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+			ret = avs_path_reset(data->path);
+			if (ret < 0)
+				dev_err(dai->dev, "reset FE path failed: %d\n", ret);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+const struct snd_soc_dai_ops avs_dai_fe_ops = {
+	.startup = avs_dai_fe_startup,
+	.shutdown = avs_dai_fe_shutdown,
+	.hw_params = avs_dai_fe_hw_params,
+	.hw_free = avs_dai_fe_hw_free,
+	.prepare = avs_dai_fe_prepare,
+	.trigger = avs_dai_fe_trigger,
+};
+
 static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
 				  loff_t *ppos)
 {
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index d3fd5e145ee1..4ea799658ed9 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -15,8 +15,6 @@
 #include "avs.h"
 #include "topology.h"
 
-const struct snd_soc_dai_ops avs_dai_fe_ops;
-
 /* Get pointer to vendor array at the specified offset. */
 #define avs_tplg_vendor_array_at(array, offset) \
 	((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset))
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (2 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 03/14] ASoC: Intel: avs: Generic PCM FE operations Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:40   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 05/14] ASoC: Intel: avs: HDA " Cezary Rojewski
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

DMIC and I2S interfaces differ in DMA operations from the HDAudio
interface. With that in mind, implement all DAI operations to handle
non-HDA BE interfaces.

To prevent code duplication in newly added code, I2S platform
registering is dynamic - makes use of specified port_mask and TDMs
array to populate as many DAIs as required.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/avs.h |   4 +
 sound/soc/intel/avs/pcm.c | 222 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 225 insertions(+), 1 deletion(-)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 14b4a780a91c..b4fd67fac17d 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -270,4 +270,8 @@ 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);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 725caab7cf28..0132305dbb29 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -112,6 +112,23 @@ static int avs_dai_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
+static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai,
+				int dma_id)
+{
+	struct snd_pcm_hw_params *fe_hw_params = NULL;
+	struct snd_soc_pcm_runtime *fe, *be;
+	struct snd_soc_dpcm *dpcm;
+
+	be = asoc_substream_to_rtd(substream);
+	for_each_dpcm_fe(be, substream->stream, dpcm) {
+		fe = dpcm->fe;
+		fe_hw_params = &fe->dpcm[substream->stream].hw_params;
+	}
+
+	return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id);
+}
+
 static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
@@ -134,6 +151,100 @@ static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *subst
 	return ret;
 }
 
+static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	kfree(data);
+}
+
+static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream,
+				       struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (data->path)
+		return 0;
+
+	/* Actual port-id comes from topology. */
+	return avs_dai_be_hw_params(substream, hw_params, dai, 0);
+}
+
+static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+
+	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (data->path) {
+		avs_path_free(data->path);
+		data->path = NULL;
+	}
+
+	return 0;
+}
+
+static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+}
+
+static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+				     struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	int ret = 0;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+		if (ret < 0)
+			dev_err(dai->dev, "run BE path failed: %d\n", ret);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		ret = avs_path_pause(data->path);
+		if (ret < 0)
+			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+			ret = avs_path_reset(data->path);
+			if (ret < 0)
+				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
+	.startup = avs_dai_nonhda_be_startup,
+	.shutdown = avs_dai_nonhda_be_shutdown,
+	.hw_params = avs_dai_nonhda_be_hw_params,
+	.hw_free = avs_dai_nonhda_be_hw_free,
+	.prepare = avs_dai_nonhda_be_prepare,
+	.trigger = avs_dai_nonhda_be_trigger,
+};
+
 static const unsigned int rates[] = {
 	8000, 11025, 12000, 16000,
 	22050, 24000, 32000, 44100,
@@ -589,7 +700,6 @@ static const struct snd_soc_component_driver avs_component_driver = {
 	.non_legacy_dai_naming	= true,
 };
 
-__maybe_unused
 static int avs_soc_component_register(struct device *dev, const char *name,
 				      const struct snd_soc_component_driver *drv,
 				      struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
@@ -611,3 +721,113 @@ static int avs_soc_component_register(struct device *dev, const char *name,
 
 	return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais);
 }
+
+static struct snd_soc_dai_driver dmic_cpu_dais[] = {
+{
+	.name = "DMIC Pin",
+	.ops = &avs_dai_nonhda_be_ops,
+	.capture = {
+		.stream_name	= "DMIC Rx",
+		.channels_min	= 1,
+		.channels_max	= 4,
+		.rates		= SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "DMIC WoV Pin",
+	.ops = &avs_dai_nonhda_be_ops,
+	.capture = {
+		.stream_name	= "DMIC WoV Rx",
+		.channels_min	= 1,
+		.channels_max	= 4,
+		.rates		= SNDRV_PCM_RATE_16000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+	},
+},
+};
+
+int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
+{
+	return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais,
+					  ARRAY_SIZE(dmic_cpu_dais));
+}
+
+static const struct snd_soc_dai_driver i2s_dai_template = {
+	.ops = &avs_dai_nonhda_be_ops,
+	.playback = {
+		.channels_min	= 1,
+		.channels_max	= 8,
+		.rates		= SNDRV_PCM_RATE_8000_192000 |
+				  SNDRV_PCM_RATE_KNOT,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FMTBIT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.channels_min	= 1,
+		.channels_max	= 8,
+		.rates		= SNDRV_PCM_RATE_8000_192000 |
+				  SNDRV_PCM_RATE_KNOT,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FMTBIT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	},
+};
+
+int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask,
+			      unsigned long *tdms)
+{
+	struct snd_soc_dai_driver *cpus, *dai;
+	size_t ssp_count, cpu_count;
+	int i, j;
+
+	ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+	cpu_count = hweight_long(port_mask);
+	if (tdms)
+		for_each_set_bit(i, &port_mask, ssp_count)
+			cpu_count += hweight_long(tdms[i]);
+
+	cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL);
+	if (!cpus)
+		return -ENOMEM;
+
+	dai = cpus;
+	for_each_set_bit(i, &port_mask, ssp_count) {
+		memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+		dai->name =
+			devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i);
+		dai->playback.stream_name =
+			devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i);
+		dai->capture.stream_name =
+			devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i);
+
+		if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+			return -ENOMEM;
+		dai++;
+	}
+
+	if (!tdms)
+		goto plat_register;
+
+	for_each_set_bit(i, &port_mask, ssp_count) {
+		for_each_set_bit(j, &tdms[i], ssp_count) {
+			memcpy(dai, &i2s_dai_template, sizeof(*dai));
+
+			dai->name =
+				devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j);
+			dai->playback.stream_name =
+				devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j);
+			dai->capture.stream_name =
+				devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j);
+
+			if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
+				return -ENOMEM;
+			dai++;
+		}
+	}
+
+plat_register:
+	return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 05/14] ASoC: Intel: avs: HDA PCM BE operations
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (3 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:45   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow Cezary Rojewski
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

HDA streaming in DSP world means enlisting HDAudio links as BE
interfaces. Another difference when compared to its DMIC and I2S friends
is lack of NHLT blob usage - no additional hardware configuration is
needed.

Similarly to I2S component, HDA populates its DAIs dynamically, here by
the means of codec->pcm_list_head. Allows for cutting the number of soc
components required to support the interface.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/avs.h |   1 +
 sound/soc/intel/avs/pcm.c | 349 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 350 insertions(+)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index b4fd67fac17d..e628f78d1864 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -273,5 +273,6 @@ 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);
 
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 0132305dbb29..9e204df97940 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -245,6 +245,155 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
 	.trigger = avs_dai_nonhda_be_trigger,
 };
 
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	return avs_dai_startup(substream, dai, false);
+}
+
+static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	return avs_dai_nonhda_be_shutdown(substream, dai);
+}
+
+static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	struct hdac_ext_stream *estream;
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (data->path)
+		return 0;
+
+	estream = substream->runtime->private_data;
+
+	return avs_dai_be_hw_params(substream, hw_params, dai,
+				    hdac_stream(estream)->stream_tag - 1);
+}
+
+static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct avs_dma_data *data;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct hdac_ext_stream *estream;
+	struct hdac_ext_link *link;
+	struct hda_codec *codec;
+
+	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	if (!data->path)
+		return 0;
+
+	estream = substream->runtime->private_data;
+	estream->link_prepared = false;
+	avs_path_free(data->path);
+	data->path = NULL;
+
+	/* clear link <-> stream mapping */
+	codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+	link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
+	if (!link)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_hdac_ext_link_clear_stream_id(link, estream->hstream.stream_tag);
+
+	return 0;
+}
+
+static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hdac_ext_stream *estream = runtime->private_data;
+	struct hdac_ext_link *link;
+	struct hda_codec *codec;
+	struct hdac_bus *bus;
+	unsigned int format_val;
+	int ret;
+
+	if (estream->link_prepared)
+		return 0;
+
+	codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+	bus = &codec->bus->core;
+	format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
+						 runtime->sample_bits, 0);
+
+	snd_hdac_ext_stream_decouple(bus, estream, true);
+	snd_hdac_ext_link_stream_reset(estream);
+	snd_hdac_ext_link_stream_setup(estream, format_val);
+
+	link = snd_hdac_ext_bus_link_at(bus, codec->core.addr);
+	if (!link)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_hdac_ext_link_set_stream_id(link, estream->hstream.stream_tag);
+
+	ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai);
+	if (ret)
+		return ret;
+
+	estream->link_prepared = true;
+	return 0;
+}
+
+static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
+				  struct snd_soc_dai *dai)
+{
+	struct hdac_ext_stream *estream;
+	struct avs_dma_data *data;
+	int ret = 0;
+
+	dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
+
+	data = snd_soc_dai_get_dma_data(dai, substream);
+	estream = substream->runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		snd_hdac_ext_link_stream_start(estream);
+
+		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
+		if (ret < 0)
+			dev_err(dai->dev, "run BE path failed: %d\n", ret);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		ret = avs_path_pause(data->path);
+		if (ret < 0)
+			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
+
+		snd_hdac_ext_link_stream_clear(estream);
+
+		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+			ret = avs_path_reset(data->path);
+			if (ret < 0)
+				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
+	.startup = avs_dai_hda_be_startup,
+	.shutdown = avs_dai_hda_be_shutdown,
+	.hw_params = avs_dai_hda_be_hw_params,
+	.hw_free = avs_dai_hda_be_hw_free,
+	.prepare = avs_dai_hda_be_prepare,
+	.trigger = avs_dai_hda_be_trigger,
+};
+
 static const unsigned int rates[] = {
 	8000, 11025, 12000, 16000,
 	22050, 24000, 32000, 44100,
@@ -831,3 +980,203 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
 plat_register:
 	return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count);
 }
+
+/* HD-Audio CPU DAI template */
+static const struct snd_soc_dai_driver hda_cpu_dai = {
+	.ops = &avs_dai_hda_be_ops,
+	.playback = {
+		.channels_min	= 1,
+		.channels_max	= 8,
+		.rates		= SNDRV_PCM_RATE_8000_192000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FMTBIT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.capture = {
+		.channels_min	= 1,
+		.channels_max	= 8,
+		.rates		= SNDRV_PCM_RATE_8000_192000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FMTBIT_S24_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
+	},
+};
+
+static void avs_component_hda_unregister_dais(struct snd_soc_component *component)
+{
+	struct snd_soc_acpi_mach *mach;
+	struct snd_soc_dai *dai, *save;
+	struct hda_codec *codec;
+	char name[32];
+
+	mach = dev_get_platdata(component->card->dev);
+	codec = mach->pdata;
+	sprintf(name, "%s-cpu", dev_name(&codec->core.dev));
+
+	for_each_component_dais_safe(component, dai, save) {
+		if (!strstr(dai->driver->name, name))
+			continue;
+
+		if (dai->playback_widget)
+			snd_soc_dapm_free_widget(dai->playback_widget);
+		if (dai->capture_widget)
+			snd_soc_dapm_free_widget(dai->capture_widget);
+		snd_soc_unregister_dai(dai);
+	}
+}
+
+static int avs_component_hda_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm;
+	struct snd_soc_dai_driver *dais;
+	struct snd_soc_acpi_mach *mach;
+	struct hda_codec *codec;
+	struct hda_pcm *pcm;
+	const char *cname;
+	int pcm_count = 0, ret, i;
+
+	mach = dev_get_platdata(component->card->dev);
+	if (!mach)
+		return -EINVAL;
+
+	codec = mach->pdata;
+	if (list_empty(&codec->pcm_list_head))
+		return -EINVAL;
+	list_for_each_entry(pcm, &codec->pcm_list_head, list)
+		pcm_count++;
+
+	dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
+			    GFP_KERNEL);
+	if (!dais)
+		return -ENOMEM;
+
+	cname = dev_name(&codec->core.dev);
+	dapm = snd_soc_component_get_dapm(component);
+	pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
+
+	for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
+		struct snd_soc_dai *dai;
+
+		memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais));
+		dais[i].id = i;
+		dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
+					      "%s-cpu%d", cname, i);
+		if (!dais[i].name) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		if (pcm->stream[0].substreams) {
+			dais[i].playback.stream_name =
+				devm_kasprintf(component->dev, GFP_KERNEL,
+					       "%s-cpu%d Tx", cname, i);
+			if (!dais[i].playback.stream_name) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+		}
+
+		if (pcm->stream[1].substreams) {
+			dais[i].capture.stream_name =
+				devm_kasprintf(component->dev, GFP_KERNEL,
+					       "%s-cpu%d Rx", cname, i);
+			if (!dais[i].capture.stream_name) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+		}
+
+		dai = snd_soc_register_dai(component, &dais[i], false);
+		if (!dai) {
+			dev_err(component->dev, "register dai for %s failed\n",
+				pcm->name);
+			ret = -EINVAL;
+			goto exit;
+		}
+
+		ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
+		if (ret < 0) {
+			dev_err(component->dev, "create widgets failed: %d\n",
+				ret);
+			goto exit;
+		}
+	}
+
+	ret = avs_component_probe(component);
+exit:
+	if (ret)
+		avs_component_hda_unregister_dais(component);
+
+	return ret;
+}
+
+static void avs_component_hda_remove(struct snd_soc_component *component)
+{
+	avs_component_hda_unregister_dais(component);
+	avs_component_remove(component);
+}
+
+static int avs_component_hda_open(struct snd_soc_component *component,
+				  struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct hdac_ext_stream *estream;
+	struct hda_codec *codec;
+
+	/* only BE DAI links are handled here */
+	if (!rtd->dai_link->no_pcm)
+		return avs_component_open(component, substream);
+
+	codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
+	estream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
+					     HDAC_EXT_STREAM_TYPE_LINK);
+	if (!estream)
+		return -EBUSY;
+
+	substream->runtime->private_data = estream;
+	return 0;
+}
+
+static int avs_component_hda_close(struct snd_soc_component *component,
+				   struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct hdac_ext_stream *estream;
+
+	/* only BE DAI links are handled here */
+	if (!rtd->dai_link->no_pcm)
+		return 0;
+
+	estream = substream->runtime->private_data;
+	snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_LINK);
+	substream->runtime->private_data = NULL;
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver avs_hda_component_driver = {
+	.name			= "avs-hda-pcm",
+	.probe			= avs_component_hda_probe,
+	.remove			= avs_component_hda_remove,
+	.open			= avs_component_hda_open,
+	.close			= avs_component_hda_close,
+	.pointer		= avs_component_pointer,
+	.mmap			= avs_component_mmap,
+	.pcm_construct		= avs_component_construct,
+	/*
+	 * hda platform component's probe() is dependent on
+	 * codec->pcm_list_head, it needs to be initialized after codec
+	 * component. remove_order is here for completeness sake
+	 */
+	.probe_order		= SND_SOC_COMP_ORDER_LATE,
+	.remove_order		= SND_SOC_COMP_ORDER_EARLY,
+	.module_get_upon_open	= 1,
+	.topology_name_prefix	= "intel/avs",
+	.non_legacy_dai_naming	= true,
+};
+
+int avs_hda_platform_register(struct avs_dev *adev, const char *name)
+{
+	return avs_soc_component_register(adev->dev, name,
+					  &avs_hda_component_driver, NULL, 0);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (4 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 05/14] ASoC: Intel: avs: HDA " Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:53   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 07/14] ASoC: Intel: avs: Prepare for firmware tracing Cezary Rojewski
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

In rare occassions, under stress conditions or hardware malfunction, DSP
firmware may fail. Software is notified about such situation with
EXCEPTION_CAUGHT notification. IPC timeout is also counted as critical
device failure. More often than not, driver can recover from such
situations by performing full reset: killing and restarting ADSP.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/Kconfig        |  1 +
 sound/soc/intel/avs/avs.h      |  4 ++
 sound/soc/intel/avs/ipc.c      | 95 +++++++++++++++++++++++++++++++++-
 sound/soc/intel/avs/messages.h |  5 ++
 4 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index c364ddf22267..05ad6bdecfc5 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -218,6 +218,7 @@ config SND_SOC_INTEL_AVS
 	select SND_HDA_EXT_CORE
 	select SND_HDA_DSP_LOADER
 	select SND_INTEL_NHLT
+	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/avs.h b/sound/soc/intel/avs/avs.h
index e628f78d1864..02c2aa1bcd5c 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -42,6 +42,7 @@ 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 coredump)(struct avs_dev *, union avs_notify_msg *);
 };
 
 #define avs_dsp_op(adev, op, ...) \
@@ -164,12 +165,15 @@ struct avs_ipc {
 	struct avs_ipc_msg rx;
 	u32 default_timeout_ms;
 	bool ready;
+	bool 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;
 };
 
 #define AVS_EIPC	EREMOTEIO
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 68aaf01edbf2..84cb411c82fa 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -14,6 +14,87 @@
 
 #define AVS_IPC_TIMEOUT_MS	300
 
+static void avs_dsp_recovery(struct avs_dev *adev)
+{
+	struct avs_soc_component *acomp;
+	unsigned int core_mask;
+	int ret;
+
+	if (adev->ipc->recovering)
+		return;
+	adev->ipc->recovering = true;
+
+	mutex_lock(&adev->comp_list_mutex);
+	/* disconnect all running streams */
+	list_for_each_entry(acomp, &adev->comp_list, node) {
+		struct snd_soc_pcm_runtime *rtd;
+		struct snd_soc_card *card;
+
+		card = acomp->base.card;
+		if (!card)
+			continue;
+
+		for_each_card_rtds(card, rtd) {
+			struct snd_pcm *pcm;
+			int dir;
+
+			pcm = rtd->pcm;
+			if (!pcm || rtd->dai_link->no_pcm)
+				continue;
+
+			for_each_pcm_streams(dir) {
+				struct snd_pcm_substream *substream;
+
+				substream = pcm->streams[dir].substream;
+				if (!substream || !substream->runtime)
+					continue;
+
+				snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+			}
+		}
+	}
+	mutex_unlock(&adev->comp_list_mutex);
+
+	/* forcibly shutdown all cores */
+	core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
+	avs_dsp_core_disable(adev, core_mask);
+
+	/* attempt dsp reboot */
+	ret = avs_dsp_boot_firmware(adev, true);
+	if (ret < 0)
+		dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
+
+	pm_runtime_mark_last_busy(adev->dev);
+	pm_runtime_enable(adev->dev);
+	pm_request_autosuspend(adev->dev);
+
+	adev->ipc->recovering = false;
+}
+
+static void avs_dsp_recovery_work(struct work_struct *work)
+{
+	struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work);
+
+	avs_dsp_recovery(to_avs_dev(ipc->dev));
+}
+
+static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+	if (adev->ipc->recovering)
+		return;
+
+	dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
+
+	/* Re-enabled on recovery completion. */
+	avs_ipc_block(adev->ipc);
+	pm_runtime_disable(adev->dev);
+
+	/* Process received notification. */
+	avs_dsp_op(adev, coredump, msg);
+
+	schedule_work(&adev->ipc->recovery_work);
+}
+
 static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 {
 	struct avs_ipc *ipc = adev->ipc;
@@ -57,6 +138,9 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 		data_size = sizeof(struct avs_notify_res_data);
 		break;
 
+	case AVS_NOTIFY_EXCEPTION_CAUGHT:
+		break;
+
 	case AVS_NOTIFY_MODULE_EVENT:
 		/* To know the total payload size, header needs to be read first. */
 		memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
@@ -84,6 +168,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 		complete(&adev->fw_ready);
 		break;
 
+	case AVS_NOTIFY_EXCEPTION_CAUGHT:
+		avs_dsp_exception_caught(adev, &msg);
+		break;
+
 	default:
 		break;
 	}
@@ -278,9 +366,10 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
 	ret = avs_ipc_wait_busy_completion(ipc, timeout);
 	if (ret) {
 		if (ret == -ETIMEDOUT) {
-			dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret);
+			union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT);
 
-			avs_ipc_block(ipc);
+			/* Same treatment as on exception, just stack_dump=0. */
+			avs_dsp_exception_caught(adev, &msg);
 		}
 		goto exit;
 	}
@@ -368,6 +457,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
 	ipc->dev = dev;
 	ipc->ready = false;
 	ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
+	INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
 	init_completion(&ipc->done_completion);
 	init_completion(&ipc->busy_completion);
 	spin_lock_init(&ipc->rx_lock);
@@ -379,4 +469,5 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
 void avs_ipc_block(struct avs_ipc *ipc)
 {
 	ipc->ready = false;
+	cancel_work_sync(&ipc->recovery_work);
 }
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 0395dd7150eb..94875a153124 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -187,6 +187,7 @@ enum avs_notify_msg_type {
 	AVS_NOTIFY_PHRASE_DETECTED = 4,
 	AVS_NOTIFY_RESOURCE_EVENT = 5,
 	AVS_NOTIFY_FW_READY = 8,
+	AVS_NOTIFY_EXCEPTION_CAUGHT = 10,
 	AVS_NOTIFY_MODULE_EVENT = 12,
 };
 
@@ -205,6 +206,10 @@ union avs_notify_msg {
 		};
 		union {
 			u32 val;
+			struct {
+				u32 core_id:2;
+				u32 stack_dump_size:16;
+			} coredump;
 		} ext;
 	};
 } __packed;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 07/14] ASoC: Intel: avs: Prepare for firmware tracing
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (5 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 08/14] ASoC: Intel: avs: D0ix power state support Cezary Rojewski
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Firmware provides its own debug functionality. While coredump is one of
these, traces are the main area of interest. kfifo is enlisted to cache
log data that is being pumped to driver through SRAM. Separate DSP
operations are declared as actual feature implementation differs between
firmware generations.

As log gathering involves usage of IPCs, add all necessary: ENABLE_LOGS
and SYSTEM_TIME.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/avs.h       | 32 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/ipc.c       |  5 +++++
 sound/soc/intel/avs/messages.c  | 31 +++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h  | 21 +++++++++++++++++++++
 sound/soc/intel/avs/registers.h |  1 +
 sound/soc/intel/avs/utils.c     | 23 +++++++++++++++++++++++
 6 files changed, 113 insertions(+)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 02c2aa1bcd5c..917a8b06cace 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>
@@ -42,6 +43,10 @@ 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 *);
 };
 
@@ -75,6 +80,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
  *
@@ -115,6 +130,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 */
@@ -279,4 +296,19 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
 			      unsigned long *tdms);
 int avs_hda_platform_register(struct avs_dev *adev, const char *name);
 
+/* 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); \
+})
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 84cb411c82fa..b535bbb5953a 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -138,6 +138,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 		data_size = sizeof(struct avs_notify_res_data);
 		break;
 
+	case AVS_NOTIFY_LOG_BUFFER_STATUS:
 	case AVS_NOTIFY_EXCEPTION_CAUGHT:
 		break;
 
@@ -168,6 +169,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 		complete(&adev->fw_ready);
 		break;
 
+	case AVS_NOTIFY_LOG_BUFFER_STATUS:
+		avs_dsp_op(adev, log_buffer_status, &msg);
+		break;
+
 	case AVS_NOTIFY_EXCEPTION_CAUGHT:
 		avs_dsp_exception_caught(adev, &msg);
 		break;
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 004da166a943..3da33150aabf 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -677,6 +677,37 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
 	return 0;
 }
 
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
+{
+	int ret;
+
+	ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+				       AVS_BASEFW_ENABLE_LOGS, log_info, size);
+	if (ret)
+		dev_err(adev->dev, "enable logs failed: %d\n", ret);
+
+	return ret;
+}
+
+int avs_ipc_set_system_time(struct avs_dev *adev)
+{
+	struct avs_sys_time sys_time;
+	int ret;
+	u64 us;
+
+	/* firmware expects UTC time in micro seconds */
+	us = ktime_to_us(ktime_get());
+	sys_time.val_l = us & UINT_MAX;
+	sys_time.val_u = us >> 32;
+
+	ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+				       AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time));
+	if (ret)
+		dev_err(adev->dev, "set system time failed: %d\n", ret);
+
+	return ret;
+}
+
 int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
 				   u8 instance_id, u32 sink_id,
 				   const struct avs_audio_format *src_fmt,
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 94875a153124..257482e160bc 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -186,6 +186,7 @@ union avs_reply_msg {
 enum avs_notify_msg_type {
 	AVS_NOTIFY_PHRASE_DETECTED = 4,
 	AVS_NOTIFY_RESOURCE_EVENT = 5,
+	AVS_NOTIFY_LOG_BUFFER_STATUS = 6,
 	AVS_NOTIFY_FW_READY = 8,
 	AVS_NOTIFY_EXCEPTION_CAUGHT = 10,
 	AVS_NOTIFY_MODULE_EVENT = 12,
@@ -203,6 +204,10 @@ union avs_notify_msg {
 				u32 msg_direction:1;
 				u32 msg_target:1;
 			};
+			struct {
+				u16 rsvd:12;
+				u16 core:4;
+			} log;
 		};
 		union {
 			u32 val;
@@ -329,12 +334,21 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
 #define AVS_BASEFW_INST_ID	0
 
 enum avs_basefw_runtime_param {
+	AVS_BASEFW_ENABLE_LOGS = 6,
 	AVS_BASEFW_FIRMWARE_CONFIG = 7,
 	AVS_BASEFW_HARDWARE_CONFIG = 8,
 	AVS_BASEFW_MODULES_INFO = 9,
 	AVS_BASEFW_LIBRARIES_INFO = 16,
+	AVS_BASEFW_SYSTEM_TIME = 20,
+};
+
+enum avs_log_enable {
+	AVS_LOG_DISABLE = 0,
+	AVS_LOG_ENABLE = 1
 };
 
+int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
+
 struct avs_fw_version {
 	u16 major;
 	u16 minor;
@@ -502,6 +516,13 @@ static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry)
 
 int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info);
 
+struct avs_sys_time {
+	u32 val_l;
+	u32 val_u;
+} __packed;
+
+int avs_ipc_set_system_time(struct avs_dev *adev);
+
 /* Module configuration */
 
 #define AVS_MIXIN_MOD_UUID \
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 3fd02389ed2b..f951d3441cdf 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -58,6 +58,7 @@
 #define AVS_UPLINK_WINDOW		AVS_FW_REGS_WINDOW
 /* HOST -> DSP communication window */
 #define AVS_DOWNLINK_WINDOW		1
+#define AVS_DEBUG_WINDOW		2
 
 /* registry I/O helpers */
 #define avs_sram_offset(adev, window_idx) \
diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c
index 6473e3ae4c6e..13611dee9787 100644
--- a/sound/soc/intel/avs/utils.c
+++ b/sound/soc/intel/avs/utils.c
@@ -7,6 +7,7 @@
 //
 
 #include <linux/firmware.h>
+#include <linux/kfifo.h>
 #include <linux/slab.h>
 #include "avs.h"
 #include "messages.h"
@@ -299,3 +300,25 @@ void avs_release_firmwares(struct avs_dev *adev)
 		kfree(entry);
 	}
 }
+
+unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len,
+				   spinlock_t *lock)
+{
+	struct __kfifo *__fifo = &fifo->kfifo;
+	unsigned long flags;
+	unsigned int l, off;
+
+	spin_lock_irqsave(lock, flags);
+	len = min(len, kfifo_avail(fifo));
+	off = __fifo->in & __fifo->mask;
+	l = min(len, kfifo_size(fifo) - off);
+
+	memcpy_fromio(__fifo->data + off, src, l);
+	memcpy_fromio(__fifo->data, src + l, len - l);
+	/* Make sure data copied from SRAM is visible to all CPUs. */
+	smp_mb();
+	__fifo->in += len;
+	spin_unlock_irqrestore(lock, flags);
+
+	return len;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 08/14] ASoC: Intel: avs: D0ix power state support
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (6 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 07/14] ASoC: Intel: avs: Prepare for firmware tracing Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 21:58   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 09/14] ASoC: Intel: avs: Event tracing Cezary Rojewski
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Audio DSP device supports D0 substates in form of D0ix, allowing for
preserving more power even when device is still considered active (D0).
When entered, certain domains which are not being currently used become
power gated. Entering and leaving D0ix is a complex process and differs
between firmware generations.

Conditions that disallow D0i3 and require immediate D0i0 transition
include but may not be limited to: IPC traffic, firmware tracing and
SRAM I/O. To make D0ix toggling sane, delay D0i3 transition and refresh
the timer each time an IPC is requrested.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/avs.h      |  14 ++++
 sound/soc/intel/avs/dsp.c      |  14 ++++
 sound/soc/intel/avs/ipc.c      | 119 ++++++++++++++++++++++++++++++++-
 sound/soc/intel/avs/messages.c |   4 +-
 4 files changed, 148 insertions(+), 3 deletions(-)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 917a8b06cace..c3323f90b693 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -22,6 +22,7 @@ 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
@@ -48,6 +49,8 @@ struct avs_dsp_ops {
 	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, ...) \
@@ -191,6 +194,9 @@ struct avs_ipc {
 	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
@@ -227,6 +233,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);
@@ -234,6 +245,9 @@ 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);
+
 /* Firmware resources management */
 
 int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index 3ff17bd22a5a..2f18b137ff42 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -152,6 +152,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
 
 	adev->core_refs[core_id]++;
 	if (adev->core_refs[core_id] == 1) {
+		/*
+		 * No cores other than main-core can be running for DSP
+		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
+		 * simply d0ix power state will no longer be attempted.
+		 */
+		ret = avs_dsp_disable_d0ix(adev);
+		if (ret && ret != -AVS_EIPC)
+			goto err_disable_d0ix;
+
 		ret = avs_dsp_enable(adev, mask);
 		if (ret)
 			goto err_enable_dsp;
@@ -160,6 +169,8 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
 	return 0;
 
 err_enable_dsp:
+	avs_dsp_enable_d0ix(adev);
+err_disable_d0ix:
 	adev->core_refs[core_id]--;
 err:
 	dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
@@ -185,6 +196,9 @@ static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
 		ret = avs_dsp_disable(adev, mask);
 		if (ret)
 			goto err;
+
+		/* Match disable_d0ix in avs_dsp_get_core(). */
+		avs_dsp_enable_d0ix(adev);
 	}
 
 	return 0;
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index b535bbb5953a..0820d8f93c7c 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -13,6 +13,82 @@
 #include "registers.h"
 
 #define AVS_IPC_TIMEOUT_MS	300
+#define AVS_D0IX_DELAY_MS	300
+
+static int
+avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
+{
+	struct avs_ipc *ipc = adev->ipc;
+	int ret;
+
+	/* Is transition required? */
+	if (ipc->in_d0ix == enable)
+		return 0;
+
+	ret = avs_dsp_op(adev, set_d0ix, enable);
+	if (ret) {
+		/* Prevent further d0ix attempts on conscious IPC failure. */
+		if (ret == -AVS_EIPC)
+			atomic_inc(&ipc->d0ix_disable_depth);
+
+		ipc->in_d0ix = false;
+		return ret;
+	}
+
+	ipc->in_d0ix = enable;
+	return 0;
+}
+
+static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+	if (atomic_read(&adev->ipc->d0ix_disable_depth))
+		return;
+
+	mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work,
+			 msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+}
+
+static void avs_dsp_d0ix_work(struct work_struct *work)
+{
+	struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work);
+
+	avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true);
+}
+
+static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+	struct avs_ipc *ipc = adev->ipc;
+
+	if (!atomic_read(&ipc->d0ix_disable_depth)) {
+		cancel_delayed_work_sync(&ipc->d0ix_work);
+		return avs_dsp_set_d0ix(adev, false);
+	}
+
+	return 0;
+}
+
+int avs_dsp_disable_d0ix(struct avs_dev *adev)
+{
+	struct avs_ipc *ipc = adev->ipc;
+
+	/* Prevent PG only on the first disable. */
+	if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) {
+		cancel_delayed_work_sync(&ipc->d0ix_work);
+		return avs_dsp_set_d0ix(adev, false);
+	}
+
+	return 0;
+}
+
+int avs_dsp_enable_d0ix(struct avs_dev *adev)
+{
+	struct avs_ipc *ipc = adev->ipc;
+
+	if (atomic_dec_and_test(&ipc->d0ix_disable_depth))
+		queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work,
+				   msecs_to_jiffies(AVS_D0IX_DELAY_MS));
+	return 0;
+}
 
 static void avs_dsp_recovery(struct avs_dev *adev)
 {
@@ -391,10 +467,35 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
 	return ret;
 }
 
+static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
+				     struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
+				     bool schedule_d0ix)
+{
+	int ret;
+
+	if (wake_d0i0) {
+		ret = avs_dsp_wake_d0i0(adev, request);
+		if (ret)
+			return ret;
+	}
+
+	ret = avs_dsp_do_send_msg(adev, request, reply, timeout);
+	if (ret)
+		return ret;
+
+	if (schedule_d0ix)
+		avs_dsp_schedule_d0ix(adev, request);
+
+	return 0;
+}
+
 int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
 			     struct avs_ipc_msg *reply, int timeout)
 {
-	return avs_dsp_do_send_msg(adev, request, reply, timeout);
+	bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
+	bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
+
+	return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix);
 }
 
 int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
@@ -403,6 +504,19 @@ int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
 	return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms);
 }
 
+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)
+{
+	return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false);
+}
+
+int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+			struct avs_ipc_msg *reply, bool wake_d0i0)
+{
+	return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
+					   wake_d0i0);
+}
+
 static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
 {
 	struct avs_ipc *ipc = adev->ipc;
@@ -463,6 +577,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
 	ipc->ready = false;
 	ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
 	INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
+	INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work);
 	init_completion(&ipc->done_completion);
 	init_completion(&ipc->busy_completion);
 	spin_lock_init(&ipc->rx_lock);
@@ -475,4 +590,6 @@ void avs_ipc_block(struct avs_ipc *ipc)
 {
 	ipc->ready = false;
 	cancel_work_sync(&ipc->recovery_work);
+	cancel_delayed_work_sync(&ipc->d0ix_work);
+	ipc->in_d0ix = false;
 }
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 3da33150aabf..6404fce8cde4 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -432,7 +432,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
 	request.data = &dx;
 	request.size = sizeof(dx);
 
-	ret = avs_dsp_send_msg(adev, &request, NULL);
+	ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
 	if (ret)
 		avs_ipc_err(adev, &request, "set dx", ret);
 
@@ -456,7 +456,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
 
 	request.header = msg.val;
 
-	ret = avs_dsp_send_msg(adev, &request, NULL);
+	ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
 	if (ret)
 		avs_ipc_err(adev, &request, "set d0ix", ret);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 09/14] ASoC: Intel: avs: Event tracing
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (7 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 08/14] ASoC: Intel: avs: D0ix power state support Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 10/14] ASoC: Intel: avs: Machine board registration Cezary Rojewski
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Define tracing macros for easy avs debug. These cover all IPC message
types: requests, replies and notifications as well as DSP-core
operations and d0ix toggling.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/Makefile |   4 +
 sound/soc/intel/avs/dsp.c    |  10 +++
 sound/soc/intel/avs/ipc.c    |  30 ++++++-
 sound/soc/intel/avs/trace.c  |  33 ++++++++
 sound/soc/intel/avs/trace.h  | 158 +++++++++++++++++++++++++++++++++++
 5 files changed, 232 insertions(+), 3 deletions(-)
 create mode 100644 sound/soc/intel/avs/trace.c
 create mode 100644 sound/soc/intel/avs/trace.h

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 62b3581d6cdb..38285e73e75d 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -4,4 +4,8 @@ snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
 		    topology.o path.o pcm.o
 snd-soc-avs-objs += cldma.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/dsp.c b/sound/soc/intel/avs/dsp.c
index 2f18b137ff42..8f111250c5b1 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -10,6 +10,7 @@
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "registers.h"
+#include "trace.h"
 
 #define AVS_ADSPCS_INTERVAL_US		500
 #define AVS_ADSPCS_TIMEOUT_US		50000
@@ -19,6 +20,9 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
 	u32 value, mask, reg;
 	int ret;
 
+	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+	trace_avs_dsp_core_op(value, core_mask, "power", power);
+
 	mask = AVS_ADSPCS_SPA_MASK(core_mask);
 	value = power ? mask : 0;
 
@@ -43,6 +47,9 @@ int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
 	u32 value, mask, reg;
 	int ret;
 
+	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+	trace_avs_dsp_core_op(value, core_mask, "reset", reset);
+
 	mask = AVS_ADSPCS_CRST_MASK(core_mask);
 	value = reset ? mask : 0;
 
@@ -64,6 +71,9 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
 	u32 value, mask, reg;
 	int ret;
 
+	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
+	trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+
 	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
 	value = stall ? mask : 0;
 
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 0820d8f93c7c..1ecf9e23cf71 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -6,11 +6,13 @@
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
+#include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/slab.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "messages.h"
 #include "registers.h"
+#include "trace.h"
 
 #define AVS_IPC_TIMEOUT_MS	300
 #define AVS_D0IX_DELAY_MS	300
@@ -175,6 +177,10 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 {
 	struct avs_ipc *ipc = adev->ipc;
 	union avs_reply_msg msg = AVS_MSG(header);
+	u64 reg;
+
+	reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+	trace_avs_ipc_reply_msg(header, reg);
 
 	ipc->rx.header = header;
 	/* Abort copying payload if request processing was unsuccessful. */
@@ -185,6 +191,7 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 			ipc->rx.size = msg.ext.large_config.data_off_size;
 
 		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size);
+		trace_avs_msg_payload(ipc->rx.data, ipc->rx.size);
 	}
 }
 
@@ -194,6 +201,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 	union avs_notify_msg msg = AVS_MSG(header);
 	size_t data_size = 0;
 	void *data = NULL;
+	u64 reg;
+
+	reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+	trace_avs_ipc_notify_msg(header, reg);
 
 	/* Ignore spurious notifications until handshake is established. */
 	if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
@@ -235,6 +246,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
 			return;
 
 		memcpy_fromio(data, avs_uplink_addr(adev), data_size);
+		trace_avs_msg_payload(data, data_size);
 	}
 
 	/* Perform notification-specific operations. */
@@ -418,9 +430,15 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
 	reinit_completion(&ipc->busy_completion);
 }
 
-static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx)
+static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs)
 {
+	u64 reg = ULONG_MAX;
+
 	tx->header |= SKL_ADSP_HIPCI_BUSY;
+	if (read_fwregs)
+		reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW));
+
+	trace_avs_request(tx, reg);
 
 	if (tx->size)
 		memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
@@ -441,7 +459,7 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
 
 	spin_lock(&ipc->rx_lock);
 	avs_ipc_msg_init(ipc, reply);
-	avs_dsp_send_tx(adev, request);
+	avs_dsp_send_tx(adev, request, true);
 	spin_unlock(&ipc->rx_lock);
 
 	ret = avs_ipc_wait_busy_completion(ipc, timeout);
@@ -473,6 +491,7 @@ static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *r
 {
 	int ret;
 
+	trace_avs_d0ix("wake", wake_d0i0, request->header);
 	if (wake_d0i0) {
 		ret = avs_dsp_wake_d0i0(adev, request);
 		if (ret)
@@ -483,6 +502,7 @@ static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *r
 	if (ret)
 		return ret;
 
+	trace_avs_d0ix("schedule", schedule_d0ix, request->header);
 	if (schedule_d0ix)
 		avs_dsp_schedule_d0ix(adev, request);
 
@@ -526,7 +546,11 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req
 
 	spin_lock(&ipc->rx_lock);
 	avs_ipc_msg_init(ipc, NULL);
-	avs_dsp_send_tx(adev, request);
+	/*
+	 * with hw still stalled, memory windows may not be
+	 * configured properly so avoid accessing SRAM
+	 */
+	avs_dsp_send_tx(adev, request, false);
 	spin_unlock(&ipc->rx_lock);
 
 	/* ROM messages must be sent before main core is unstalled */
diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c
new file mode 100644
index 000000000000..fcb7cfc823d6
--- /dev/null
+++ b/sound/soc/intel/avs/trace.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//         Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/types.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#define BYTES_PER_LINE 16
+#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */	\
+			/ (2 * BYTES_PER_LINE + 4) /* chars per line */	\
+			* BYTES_PER_LINE)
+
+void trace_avs_msg_payload(const void *data, size_t size)
+{
+	size_t remaining = size;
+	size_t offset = 0;
+
+	while (remaining > 0) {
+		u32 chunk;
+
+		chunk = min(remaining, (size_t)MAX_CHUNK_SIZE);
+		trace_avs_ipc_msg_payload(data, chunk, offset, size);
+
+		remaining -= chunk;
+		offset += chunk;
+	}
+}
diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h
new file mode 100644
index 000000000000..9089ce8d135b
--- /dev/null
+++ b/sound/soc/intel/avs/trace.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM intel_avs
+
+#if !defined(_TRACE_INTEL_AVS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_INTEL_AVS_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(avs_dsp_core_op,
+
+	TP_PROTO(unsigned int reg, unsigned int mask, const char *op, bool flag),
+
+	TP_ARGS(reg, mask, op, flag),
+
+	TP_STRUCT__entry(
+		__field(unsigned int,	reg	)
+		__field(unsigned int,	mask	)
+		__string(op,		op	)
+		__field(bool,		flag	)
+	),
+
+	TP_fast_assign(
+		__entry->reg = reg;
+		__entry->mask = mask;
+		__assign_str(op, op);
+		__entry->flag = flag;
+	),
+
+	TP_printk("%s: %d, core mask: 0x%X, prev state: 0x%08X",
+		  __get_str(op), __entry->flag, __entry->mask, __entry->reg)
+);
+
+#ifndef __TRACE_INTEL_AVS_TRACE_HELPER
+#define __TRACE_INTEL_AVS_TRACE_HELPER
+
+#ifdef CONFIG_FTRACE
+void trace_avs_msg_payload(const void *data, size_t size);
+#else
+static inline void trace_avs_msg_payload(const void *data, size_t size) {};
+#endif
+
+#define trace_avs_request(msg, fwregs) \
+({ \
+	trace_avs_ipc_request_msg((msg)->header, fwregs); \
+	trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_reply(msg, fwregs) \
+({ \
+	trace_avs_ipc_reply_msg((msg)->header, fwregs); \
+	trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+
+#define trace_avs_notify(msg, fwregs) \
+({ \
+	trace_avs_ipc_notify_msg((msg)->header, fwregs); \
+	trace_avs_msg_payload((msg)->data, (msg)->size); \
+})
+#endif
+
+DECLARE_EVENT_CLASS(avs_ipc_msg_hdr,
+
+	TP_PROTO(u64 header, u64 fwregs),
+
+	TP_ARGS(header, fwregs),
+
+	TP_STRUCT__entry(
+		__field(u64,	header)
+		__field(u64,	fwregs)
+	),
+
+	TP_fast_assign(
+		__entry->header = header;
+		__entry->fwregs = fwregs;
+	),
+
+	TP_printk("primary: 0x%08X, extension: 0x%08X,\n"
+		  "fwstatus: 0x%08X, fwerror: 0x%08X",
+		  lower_32_bits(__entry->header), upper_32_bits(__entry->header),
+		  lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs))
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg,
+	TP_PROTO(u64 header, u64 fwregs),
+	TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg,
+	TP_PROTO(u64 header, u64 fwregs),
+	TP_ARGS(header, fwregs)
+);
+
+DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg,
+	TP_PROTO(u64 header, u64 fwregs),
+	TP_ARGS(header, fwregs)
+);
+
+TRACE_EVENT_CONDITION(avs_ipc_msg_payload,
+
+	TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total),
+
+	TP_ARGS(data, size, offset, total),
+
+	TP_CONDITION(data && size),
+
+	TP_STRUCT__entry(
+		__dynamic_array(u8,	buf,	size	)
+		__field(size_t,		offset		)
+		__field(size_t,		pos		)
+		__field(size_t,		total		)
+	),
+
+	TP_fast_assign(
+		memcpy(__get_dynamic_array(buf), data + offset, size);
+		__entry->offset = offset;
+		__entry->pos = offset + size;
+		__entry->total = total;
+	),
+
+	TP_printk("range %zu-%zu out of %zu bytes%s",
+		  __entry->offset, __entry->pos, __entry->total,
+		  __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4,
+				   __get_dynamic_array(buf),
+				   __get_dynamic_array_len(buf), false))
+);
+
+TRACE_EVENT(avs_d0ix,
+
+	TP_PROTO(const char *op, bool proceed, u64 header),
+
+	TP_ARGS(op, proceed, header),
+
+	TP_STRUCT__entry(
+		__string(op,	op	)
+		__field(bool,	proceed	)
+		__field(u64,	header	)
+	),
+
+	TP_fast_assign(
+		__assign_str(op, op);
+		__entry->proceed = proceed;
+		__entry->header = header;
+	),
+
+	TP_printk("%s%s for request: 0x%08X 0x%08X",
+		  __entry->proceed ? "" : "ignore ", __get_str(op),
+		  lower_32_bits(__entry->header), upper_32_bits(__entry->header))
+);
+
+#endif /* _TRACE_INTEL_AVS_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (8 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 09/14] ASoC: Intel: avs: Event tracing Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 22:12   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 11/14] ASoC: Intel: avs: PCI driver implementation Cezary Rojewski
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

AVS driver operates with granular audio card division in mind.
Super-card approach (e.g.: I2S, DMIC and HDA DAIs combined) is
deprecated in favour of individual cards - one per each device. This
provides necessary dynamism, especially for configurations with number
of codecs present and makes it easier to survive auxiliary devices
failures - one card failing to probe does not prevent others from
succeeding.

All boards spawned by AVS are unregistered on ->remove(). This includes
dummy codecs such as DMIC.

As all machine boards found in sound/soc/intel/boards are irreversibly
tied to 'super-card' approach, new boards are going to be introduced.
This temporarily increases number of boards available under /intel
directory until skylake-driver becomes deprecated and removed.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/Makefile          |   2 +-
 sound/soc/intel/avs/avs.h             |   3 +
 sound/soc/intel/avs/board_selection.c | 463 ++++++++++++++++++++++++++
 3 files changed, 467 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/board_selection.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 38285e73e75d..592d4dc02c56 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,7 +1,7 @@
 # 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 pcm.o
+		    topology.o path.o pcm.o board_selection.o
 snd-soc-avs-objs += cldma.o
 
 snd-soc-avs-objs += trace.o
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index c3323f90b693..12846ad93efe 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -310,6 +310,9 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
 			      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,
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
new file mode 100644
index 000000000000..711eef461a74
--- /dev/null
+++ b/sound/soc/intel/avs/board_selection.c
@@ -0,0 +1,463 @@
+// 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 ssp_loopback_test;
+module_param_named(ssp_loopback, ssp_loopback_test, bool, 0444);
+MODULE_PARM_DESC(ssp_loopback, "SSP loopback test 0=disabled, 1=enabled");
+
+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 kbl_r_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",
+		.link_mask = AVS_SSP(0),
+		.tplg_filename = "skl-rt286-tplg.bin",
+	},
+	{
+		.id = "10508825",
+		.drv_name = "avs_nau8825",
+		.link_mask = AVS_SSP(1),
+		.tplg_filename = "skl-nau8825-tplg.bin",
+	},
+	{
+		.id = "INT343B",
+		.drv_name = "avs_ssm4567",
+		.link_mask = AVS_SSP(0),
+		.tplg_filename = "skl-ssm4567-tplg.bin",
+	},
+	{
+		.id = "MX98357A",
+		.drv_name = "avs_max98357a",
+		.link_mask = AVS_SSP(0),
+		.tplg_filename = "skl-max98357a-tplg.bin",
+	},
+	{},
+};
+
+static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
+	{
+		.id = "INT343A",
+		.drv_name = "avs_rt286",
+		.link_mask = AVS_SSP(0),
+		.quirk_data = &kbl_dmi_table,
+		.machine_quirk = dmi_match_quirk,
+		.tplg_filename = "kbl-rt286-tplg.bin",
+	},
+	{
+		.id = "INT343A",
+		.drv_name = "avs_rt298",
+		.link_mask = AVS_SSP(0),
+		.quirk_data = &kbl_r_dmi_table,
+		.machine_quirk = dmi_match_quirk,
+		.tplg_filename = "kblr-rt298-tplg.bin",
+	},
+	{
+		.id = "MX98373",
+		.drv_name = "avs_max98373",
+		.link_mask = AVS_SSP(0),
+		.tplg_filename = "kbl-max98373-tplg.bin",
+	},
+	{
+		.id = "DLGS7219",
+		.drv_name = "avs_da7219",
+		.link_mask = AVS_SSP(1),
+		.tplg_filename = "kbl-da7219-tplg.bin",
+	},
+	{},
+};
+
+static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
+	{
+		.id = "INT343A",
+		.drv_name = "avs_rt298",
+		.link_mask = AVS_SSP(5),
+		.tplg_filename = "apl-rt298-tplg.bin",
+	},
+	{
+		.id = "INT34C3",
+		.drv_name = "avs_tdf8532",
+		.link_mask = AVS_SSP_RANGE(0, 5),
+		.pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
+		.tplg_filename = "apl-tdf8532-tplg.bin",
+	},
+	{
+		.id = "MX98357A",
+		.drv_name = "avs_max98357a",
+		.link_mask = AVS_SSP(5),
+		.tplg_filename = "apl-max98357a-tplg.bin",
+	},
+	{
+		.id = "DLGS7219",
+		.drv_name = "avs_da7219",
+		.link_mask = AVS_SSP(1),
+		.tplg_filename = "apl-da7219-tplg.bin",
+	},
+	{},
+};
+
+static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
+	{
+		.id = "INT343A",
+		.drv_name = "avs_rt298",
+		.link_mask = AVS_SSP(2),
+		.tplg_filename = "gml-rt298-tplg.bin",
+	},
+	{},
+};
+
+static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(0),
+		.tplg_filename = "avs_ssp_test.bin",
+	},
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(1),
+		.tplg_filename = "avs_ssp_test.bin",
+	},
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(2),
+		.tplg_filename = "avs_ssp_test.bin",
+	},
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(3),
+		.tplg_filename = "avs_ssp_test.bin",
+	},
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(4),
+		.tplg_filename = "avs_ssp_test.bin",
+	},
+	{
+		.drv_name = "avs_ssp_test",
+		.link_mask = AVS_SSP(5),
+		.tplg_filename = "avs_ssp_test.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->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->link_mask));
+		return -ENODEV;
+	}
+
+	name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
+			      mach->link_mask);
+	if (!name)
+		return -ENOMEM;
+
+	ret = avs_i2s_platform_register(adev, name, mach->link_mask, mach->pdata);
+	if (ret < 0)
+		return ret;
+
+	mach->mach_params.platform = name;
+
+	board = platform_device_register_data(NULL, mach->drv_name, mach->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, board);
+	if (ret < 0) {
+		platform_device_unregister(board);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int avs_register_i2s_boards(struct avs_dev *adev)
+{
+	const struct avs_acpi_boards *boards;
+	struct snd_soc_acpi_mach *mach;
+	int ret;
+
+	if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) {
+		dev_dbg(adev->dev, "no I2S endpoints present\n");
+		return 0;
+	}
+
+	if (ssp_loopback_test) {
+		int i, num_ssps;
+
+		num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+		/* constrain just in case FW says there can be more SSPs than possible */
+		num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps);
+
+		mach = avs_test_i2s_machines;
+
+		for (i = 0; i < num_ssps; i++) {
+			ret = avs_register_i2s_board(adev, &mach[i]);
+			if (ret < 0)
+				dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name,
+					 ret);
+		}
+		return 0;
+	}
+
+	boards = avs_get_i2s_boards(adev);
+	if (!boards) {
+		dev_dbg(adev->dev, "no I2S endpoints supported\n");
+		return 0;
+	}
+
+	for (mach = boards->machs; mach->id[0]; mach++) {
+		if (!acpi_dev_present(mach->id, NULL, -1))
+			continue;
+
+		if (mach->machine_quirk)
+			if (!mach->machine_quirk(mach))
+				continue;
+
+		ret = avs_register_i2s_board(adev, mach);
+		if (ret < 0)
+			dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret);
+	}
+
+	return 0;
+}
+
+static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
+{
+	struct snd_soc_acpi_mach mach = {{0}};
+	struct platform_device *board;
+	struct hdac_device *hdev = &codec->core;
+	char *pname;
+	int ret, id;
+
+	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
+	if (!pname)
+		return -ENOMEM;
+
+	ret = avs_hda_platform_register(adev, pname);
+	if (ret < 0)
+		return ret;
+
+	mach.pdata = codec;
+	mach.mach_params.platform = pname;
+	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
+					    hdev->vendor_id);
+	if (!mach.tplg_filename)
+		return -ENOMEM;
+
+	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
+	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
+					      sizeof(mach));
+	if (IS_ERR(board)) {
+		dev_err(adev->dev, "hda 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_hda_boards(struct avs_dev *adev)
+{
+	struct hdac_bus *bus = &adev->base.core;
+	struct hdac_device *hdev;
+	int ret;
+
+	if (!bus->num_codecs) {
+		dev_dbg(adev->dev, "no HDA endpoints present\n");
+		return 0;
+	}
+
+	list_for_each_entry(hdev, &bus->codec_list, list) {
+		struct hda_codec *codec;
+
+		codec = dev_to_hda_codec(&hdev->dev);
+
+		ret = avs_register_hda_board(adev, codec);
+		if (ret < 0)
+			dev_warn(adev->dev, "register hda-%08x failed: %d\n",
+				 codec->core.vendor_id, ret);
+	}
+
+	return 0;
+}
+
+int avs_register_all_boards(struct avs_dev *adev)
+{
+	int ret;
+
+	ret = avs_register_dmic_board(adev);
+	if (ret < 0)
+		dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n",
+			 ret);
+
+	ret = avs_register_i2s_boards(adev);
+	if (ret < 0)
+		dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n",
+			 ret);
+
+	ret = avs_register_hda_boards(adev);
+	if (ret < 0)
+		dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n",
+			 ret);
+
+	return 0;
+}
+
+void avs_unregister_all_boards(struct avs_dev *adev)
+{
+	snd_soc_unregister_component(adev->dev);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 11/14] ASoC: Intel: avs: PCI driver implementation
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (9 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 10/14] ASoC: Intel: avs: Machine board registration Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 12/14] ASoC: Intel: avs: Power management Cezary Rojewski
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

HD-Audio bus is a PCI device. Add all functions necessary to probe such
device along with its removal sequence. Behaviour implemented for all
standard operations is similar to existing solutions: sound/pci/hda and
sound/soc/intel/skylake.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/Kconfig         |   3 +-
 sound/soc/intel/avs/avs.h       |   1 +
 sound/soc/intel/avs/core.c      | 494 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/dsp.c       |   3 -
 sound/soc/intel/avs/registers.h |   1 +
 5 files changed, 498 insertions(+), 4 deletions(-)

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 05ad6bdecfc5..932b3a4ff11e 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -215,9 +215,10 @@ config SND_SOC_INTEL_AVS
 	depends on COMMON_CLK
 	select SND_SOC_ACPI
 	select SND_SOC_TOPOLOGY
+	select SND_HDA
 	select SND_HDA_EXT_CORE
 	select SND_HDA_DSP_LOADER
-	select SND_INTEL_NHLT
+	select SND_INTEL_DSP_CONFIG
 	select WANT_DEV_COREDUMP
 	help
 	  Enable support for Intel(R) cAVS 1.5 platforms with DSP
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 12846ad93efe..67a80d56c7ae 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -126,6 +126,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;
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index a4d063d12fec..93180c22032d 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -14,9 +14,17 @@
 // foundation of this driver
 //
 
+#include <linux/module.h>
 #include <linux/pci.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
 #include <sound/hdaudio.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
 #include "avs.h"
+#include "cldma.h"
 
 static void
 avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
@@ -59,3 +67,489 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
 	value = enable ? AZX_VS_EM2_L1SEN : 0;
 	snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
 }
+
+static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
+{
+	unsigned int cp_streams, pb_streams;
+	unsigned int gcap;
+
+	gcap = snd_hdac_chip_readw(bus, GCAP);
+	cp_streams = (gcap >> 8) & 0x0F;
+	pb_streams = (gcap >> 12) & 0x0F;
+	bus->num_streams = cp_streams + pb_streams;
+
+	snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
+	snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
+
+	return snd_hdac_bus_alloc_stream_pages(bus);
+}
+
+static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+	struct hdac_ext_link *hlink;
+	bool ret;
+
+	avs_hdac_clock_gating_enable(bus, false);
+	ret = snd_hdac_bus_init_chip(bus, full_reset);
+
+	/* Reset stream-to-link mapping */
+	list_for_each_entry(hlink, &bus->hlink_list, list)
+		writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
+
+	avs_hdac_clock_gating_enable(bus, true);
+
+	/* Set DUM bit to address incorrect position reporting for capture
+	 * streams. In order to do so, CTRL needs to be out of reset state
+	 */
+	snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM);
+
+	return ret;
+}
+
+static int probe_codec(struct hdac_bus *bus, int addr)
+{
+	struct hda_codec *codec;
+	unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+			   (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+	unsigned int res = -1;
+	int ret;
+
+	mutex_lock(&bus->cmd_mutex);
+	snd_hdac_bus_send_cmd(bus, cmd);
+	snd_hdac_bus_get_response(bus, addr, &res);
+	mutex_unlock(&bus->cmd_mutex);
+	if (res == -1)
+		return -EIO;
+
+	dev_dbg(bus->dev, "codec #%d probed OK: 0x%x\n", addr, res);
+
+	codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "hdaudioB%dD%d", bus->idx, addr);
+	if (IS_ERR(codec)) {
+		dev_err(bus->dev, "init codec failed: %ld\n", PTR_ERR(codec));
+		return PTR_ERR(codec);
+	}
+	/*
+	 * Allow avs_core suspend by forcing suspended state on all
+	 * of its codec child devices. Component interested in
+	 * dealing with hda codecs directly takes pm responsibilities
+	 */
+	pm_runtime_set_suspended(hda_codec_dev(codec));
+
+	/* configure effectively creates new ASoC component */
+	ret = snd_hda_codec_configure(codec);
+	if (ret < 0) {
+		dev_err(bus->dev, "failed to config codec %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus)
+{
+	int c;
+
+	/* First try to probe all given codec slots */
+	for (c = 0; c < HDA_MAX_CODECS; c++) {
+		if (!(bus->codec_mask & BIT(c)))
+			continue;
+
+		if (!probe_codec(bus, c))
+			/* success, continue probing */
+			continue;
+
+		/*
+		 * Some BIOSen give you wrong codec addresses
+		 * that don't exist
+		 */
+		dev_warn(bus->dev, "Codec #%d probe error; disabling it...\n", c);
+		bus->codec_mask &= ~BIT(c);
+		/*
+		 * More badly, accessing to a non-existing
+		 * codec often screws up the controller bus,
+		 * and disturbs the further communications.
+		 * Thus if an error occurs during probing,
+		 * better to reset the controller bus to get
+		 * back to the sanity state.
+		 */
+		snd_hdac_bus_stop_chip(bus);
+		avs_hdac_bus_init_chip(bus, true);
+	}
+}
+
+static void avs_hda_probe_work(struct work_struct *work)
+{
+	struct avs_dev *adev = container_of(work, struct avs_dev, probe_work);
+	struct hdac_bus *bus = &adev->base.core;
+	struct hdac_ext_link *hlink;
+	int ret;
+
+	pm_runtime_set_active(bus->dev); /* clear runtime_error flag */
+
+	ret = snd_hdac_i915_init(bus);
+	if (ret < 0)
+		dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret);
+
+	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+	avs_hdac_bus_init_chip(bus, true);
+	avs_hdac_bus_probe_codecs(bus);
+	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+	/* with all codecs probed, links can be powered down */
+	list_for_each_entry(hlink, &bus->hlink_list, list)
+		snd_hdac_ext_bus_link_put(bus, hlink);
+
+	snd_hdac_ext_bus_ppcap_enable(bus, true);
+	snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+	ret = avs_dsp_first_boot_firmware(adev);
+	if (ret < 0)
+		return;
+
+	adev->nhlt = intel_nhlt_init(adev->dev);
+	if (!adev->nhlt)
+		dev_info(bus->dev, "platform has no NHLT\n");
+
+	avs_register_all_boards(adev);
+
+	/* configure PM */
+	pm_runtime_set_autosuspend_delay(bus->dev, 2000);
+	pm_runtime_use_autosuspend(bus->dev);
+	pm_runtime_mark_last_busy(bus->dev);
+	pm_runtime_put_autosuspend(bus->dev);
+	pm_runtime_allow(bus->dev);
+}
+
+static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size)
+{
+	u64 prev_pos, pos, num_bytes;
+
+	div64_u64_rem(stream->curr_pos, buffer_size, &prev_pos);
+	pos = snd_hdac_stream_get_pos_posbuf(stream);
+
+	if (pos < prev_pos)
+		num_bytes = (buffer_size - prev_pos) +  pos;
+	else
+		num_bytes = pos - prev_pos;
+
+	stream->curr_pos += num_bytes;
+}
+
+/* called from IRQ */
+static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream)
+{
+	if (stream->substream) {
+		snd_pcm_period_elapsed(stream->substream);
+	} else if (stream->cstream) {
+		u64 buffer_size = stream->cstream->runtime->buffer_size;
+
+		hdac_stream_update_pos(stream, buffer_size);
+		snd_compr_fragment_elapsed(stream->cstream);
+	}
+}
+
+static irqreturn_t hdac_bus_irq_handler(int irq, void *context)
+{
+	struct hdac_bus *bus = context;
+	u32 mask, int_enable;
+	u32 status;
+	int ret = IRQ_NONE;
+
+	if (!pm_runtime_active(bus->dev))
+		return ret;
+
+	spin_lock(&bus->reg_lock);
+
+	status = snd_hdac_chip_readl(bus, INTSTS);
+	if (status == 0 || status == UINT_MAX) {
+		spin_unlock(&bus->reg_lock);
+		return ret;
+	}
+
+	/* clear rirb int */
+	status = snd_hdac_chip_readb(bus, RIRBSTS);
+	if (status & RIRB_INT_MASK) {
+		if (status & RIRB_INT_RESPONSE)
+			snd_hdac_bus_update_rirb(bus);
+		snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+	}
+
+	mask = (0x1 << bus->num_streams) - 1;
+
+	status = snd_hdac_chip_readl(bus, INTSTS);
+	status &= mask;
+	if (status) {
+		/* Disable stream interrupts; Re-enable in bottom half */
+		int_enable = snd_hdac_chip_readl(bus, INTCTL);
+		snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask)));
+		ret = IRQ_WAKE_THREAD;
+	} else {
+		ret = IRQ_HANDLED;
+	}
+
+	spin_unlock(&bus->reg_lock);
+	return ret;
+}
+
+static irqreturn_t hdac_bus_irq_thread(int irq, void *context)
+{
+	struct hdac_bus *bus = context;
+	u32 status;
+	u32 int_enable;
+	u32 mask;
+	unsigned long flags;
+
+	status = snd_hdac_chip_readl(bus, INTSTS);
+
+	snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream);
+
+	/* Re-enable stream interrupts */
+	mask = (0x1 << bus->num_streams) - 1;
+	spin_lock_irqsave(&bus->reg_lock, flags);
+	int_enable = snd_hdac_chip_readl(bus, INTCTL);
+	snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask));
+	spin_unlock_irqrestore(&bus->reg_lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int avs_hdac_acquire_irq(struct avs_dev *adev)
+{
+	struct hdac_bus *bus = &adev->base.core;
+	struct pci_dev *pci = to_pci_dev(bus->dev);
+	int ret;
+
+	/* request one and check that we only got one interrupt */
+	ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY);
+	if (ret != 1) {
+		dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret);
+		return ret;
+	}
+
+	ret = pci_request_irq(pci, 0, hdac_bus_irq_handler, hdac_bus_irq_thread, bus,
+			      KBUILD_MODNAME);
+	if (ret < 0) {
+		dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret);
+		goto free_vector;
+	}
+
+	ret = pci_request_irq(pci, 0, avs_dsp_irq_handler, avs_dsp_irq_thread, adev,
+			      KBUILD_MODNAME);
+	if (ret < 0) {
+		dev_err(adev->dev, "Failed to request IPC IRQ handler: %d\n", ret);
+		goto free_stream_irq;
+	}
+
+	return 0;
+
+free_stream_irq:
+	pci_free_irq(pci, 0, bus);
+free_vector:
+	pci_free_irq_vectors(pci);
+	return ret;
+}
+
+static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct pci_device_id *id)
+{
+	struct hda_bus *bus = &adev->base;
+	struct avs_ipc *ipc;
+	struct device *dev = &pci->dev;
+	int ret;
+
+	ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, NULL);
+	if (ret < 0)
+		return ret;
+
+	bus->core.use_posbuf = 1;
+	bus->core.bdl_pos_adj = 0;
+	bus->core.sync_write = 1;
+	bus->pci = pci;
+	bus->mixer_assigned = -1;
+	mutex_init(&bus->prepare_mutex);
+
+	ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
+	if (!ipc)
+		return -ENOMEM;
+	ret = avs_ipc_init(ipc, dev);
+	if (ret < 0)
+		return ret;
+
+	adev->dev = dev;
+	adev->spec = (const struct avs_spec *)id->driver_data;
+	adev->ipc = ipc;
+	adev->hw_cfg.dsp_cores = hweight_long(AVS_MAIN_CORE_MASK);
+	INIT_WORK(&adev->probe_work, avs_hda_probe_work);
+	INIT_LIST_HEAD(&adev->comp_list);
+	INIT_LIST_HEAD(&adev->path_list);
+	INIT_LIST_HEAD(&adev->fw_list);
+	init_completion(&adev->fw_ready);
+	spin_lock_init(&adev->path_list_lock);
+	mutex_init(&adev->modres_mutex);
+	mutex_init(&adev->comp_list_mutex);
+	mutex_init(&adev->path_mutex);
+
+	return 0;
+}
+
+static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	struct hdac_bus *bus;
+	struct avs_dev *adev;
+	struct device *dev = &pci->dev;
+	int ret;
+
+	ret = snd_intel_dsp_driver_probe(pci);
+	if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST)
+		return -ENODEV;
+
+	ret = pcim_enable_device(pci);
+	if (ret < 0)
+		return ret;
+
+	adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
+	if (!adev)
+		return -ENOMEM;
+	ret = avs_bus_init(adev, pci, id);
+	if (ret < 0) {
+		dev_err(dev, "failed to init avs bus: %d\n", ret);
+		return ret;
+	}
+
+	ret = pci_request_regions(pci, "AVS HDAudio");
+	if (ret < 0)
+		return ret;
+
+	bus = &adev->base.core;
+	bus->addr = pci_resource_start(pci, 0);
+	bus->remap_addr = pci_ioremap_bar(pci, 0);
+	if (!bus->remap_addr) {
+		dev_err(bus->dev, "ioremap error\n");
+		ret = -ENXIO;
+		goto err_remap_bar0;
+	}
+
+	adev->dsp_ba = pci_ioremap_bar(pci, 4);
+	if (!adev->dsp_ba) {
+		dev_err(bus->dev, "ioremap error\n");
+		ret = -ENXIO;
+		goto err_remap_bar4;
+	}
+
+	snd_hdac_bus_parse_capabilities(bus);
+	if (bus->mlcap)
+		snd_hdac_ext_bus_get_ml_capabilities(bus);
+
+	if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
+		dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+	} else {
+		dma_set_mask(dev, DMA_BIT_MASK(32));
+		dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+	}
+
+	ret = avs_hdac_bus_init_streams(bus);
+	if (ret < 0) {
+		dev_err(dev, "failed to init streams: %d\n", ret);
+		goto err_init_streams;
+	}
+
+	ret = avs_hdac_acquire_irq(adev);
+	if (ret < 0) {
+		dev_err(bus->dev, "failed to acquire irq: %d\n", ret);
+		goto err_acquire_irq;
+	}
+
+	pci_set_master(pci);
+	pci_set_drvdata(pci, bus);
+	device_disable_async_suspend(dev);
+
+	schedule_work(&adev->probe_work);
+
+	return 0;
+
+err_acquire_irq:
+	snd_hdac_bus_free_stream_pages(bus);
+	snd_hdac_stream_free_all(bus);
+err_init_streams:
+	iounmap(adev->dsp_ba);
+err_remap_bar4:
+	iounmap(bus->remap_addr);
+err_remap_bar0:
+	pci_release_regions(pci);
+	return ret;
+}
+
+static void avs_pci_remove(struct pci_dev *pci)
+{
+	struct hdac_device *hdev, *save;
+	struct hdac_bus *bus = pci_get_drvdata(pci);
+	struct avs_dev *adev = hdac_to_avs(bus);
+
+	cancel_work_sync(&adev->probe_work);
+	avs_ipc_block(adev->ipc);
+
+	avs_unregister_all_boards(adev);
+
+	if (adev->nhlt)
+		intel_nhlt_free(adev->nhlt);
+
+	if (avs_platattr_test(adev, CLDMA))
+		hda_cldma_free(&code_loader);
+
+	snd_hdac_stop_streams_and_chip(bus);
+	avs_dsp_op(adev, int_control, false);
+	snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+	/* it is safe to remove all codecs from the system now */
+	list_for_each_entry_safe(hdev, save, &bus->codec_list, list)
+		snd_hda_codec_unregister(hdac_to_hda_codec(hdev));
+
+	snd_hdac_bus_free_stream_pages(bus);
+	snd_hdac_stream_free_all(bus);
+	/* reverse ml_capabilities */
+	snd_hdac_link_free_all(bus);
+	snd_hdac_ext_bus_exit(bus);
+
+	avs_dsp_core_disable(adev, GENMASK(adev->hw_cfg.dsp_cores - 1, 0));
+	snd_hdac_ext_bus_ppcap_enable(bus, false);
+
+	/* snd_hdac_stop_streams_and_chip does that already? */
+	snd_hdac_bus_stop_chip(bus);
+	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+	if (bus->audio_component)
+		snd_hdac_i915_exit(bus);
+
+	avs_module_info_free(adev);
+	pci_free_irq(pci, 0, adev);
+	pci_free_irq(pci, 0, bus);
+	pci_free_irq_vectors(pci);
+	iounmap(bus->remap_addr);
+	iounmap(adev->dsp_ba);
+	pci_release_regions(pci);
+
+	/* Firmware is not needed anymore */
+	avs_release_firmwares(adev);
+
+	/* pm_runtime_forbid() can rpm_resume() which we do not want */
+	pm_runtime_disable(&pci->dev);
+	pm_runtime_forbid(&pci->dev);
+	pm_runtime_enable(&pci->dev);
+	pm_runtime_get_noresume(&pci->dev);
+}
+
+static const struct pci_device_id avs_ids[] = {
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(pci, avs_ids);
+
+static struct pci_driver avs_pci_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = avs_ids,
+	.probe = avs_pci_probe,
+	.remove = avs_pci_remove,
+};
+module_pci_driver(avs_pci_driver);
+
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>");
+MODULE_DESCRIPTION("Intel cAVS sound driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index 8f111250c5b1..06d2f7af520f 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -6,7 +6,6 @@
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
-#include <linux/module.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "registers.h"
@@ -322,5 +321,3 @@ int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
 	ida_free(&adev->ppl_ida, instance_id);
 	return ret;
 }
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index f951d3441cdf..b2100dc630e4 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -14,6 +14,7 @@
 #define AZX_PGCTL_LSRMD_MASK		BIT(4)
 #define AZX_CGCTL_MISCBDCGE_MASK	BIT(6)
 #define AZX_VS_EM2_L1SEN		BIT(13)
+#define AZX_VS_EM2_DUM			BIT(23)
 
 /* Intel HD Audio General DSP Registers */
 #define AVS_ADSP_GEN_BASE		0x0
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 12/14] ASoC: Intel: avs: Power management
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (10 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 11/14] ASoC: Intel: avs: PCI driver implementation Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 22:18   ` Pierre-Louis Bossart
  2022-04-26 17:23 ` [PATCH 13/14] ASoC: Intel: avs: SKL-based platforms support Cezary Rojewski
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

To preserve power during sleep operations, handle suspend (S3),
hibernation (S4) and runtime (RTD3) transitions. As flow for all of
is shared, define common handlers to reduce code size.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/core.c | 125 +++++++++++++++++++++++++++++++++++++
 1 file changed, 125 insertions(+)

diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 93180c22032d..c2f8fb87cfc2 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -536,6 +536,128 @@ static void avs_pci_remove(struct pci_dev *pci)
 	pm_runtime_get_noresume(&pci->dev);
 }
 
+static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power)
+{
+	struct hdac_bus *bus = &adev->base.core;
+	int ret;
+
+	flush_work(&adev->probe_work);
+
+	snd_hdac_ext_bus_link_power_down_all(bus);
+
+	ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
+	/*
+	 * pm_runtime is blocked on DSP failure but system-wide suspend is not.
+	 * Do not block entire system from suspending if that's the case.
+	 */
+	if (ret && ret != -EPERM) {
+		dev_err(adev->dev, "set dx failed: %d\n", ret);
+		return AVS_IPC_RET(ret);
+	}
+
+	avs_dsp_op(adev, int_control, false);
+	snd_hdac_ext_bus_ppcap_int_enable(bus, false);
+
+	ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+	if (ret < 0) {
+		dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
+		return ret;
+	}
+
+	snd_hdac_ext_bus_ppcap_enable(bus, false);
+	/* disable LP SRAM retention */
+	avs_hda_power_gating_enable(adev, false);
+	snd_hdac_bus_stop_chip(bus);
+	/* disable CG when putting controller to reset */
+	avs_hdac_clock_gating_enable(bus, false);
+	snd_hdac_bus_enter_link_reset(bus);
+	avs_hdac_clock_gating_enable(bus, true);
+
+	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
+
+	return 0;
+}
+
+static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
+{
+	struct hdac_bus *bus = &adev->base.core;
+	struct hdac_ext_link *hlink;
+	int ret;
+
+	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
+	avs_hdac_bus_init_chip(bus, true);
+
+	snd_hdac_ext_bus_ppcap_enable(bus, true);
+	snd_hdac_ext_bus_ppcap_int_enable(bus, true);
+
+	ret = avs_dsp_boot_firmware(adev, purge);
+	if (ret < 0) {
+		dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+		return ret;
+	}
+
+	/* turn off the links that were off before suspend */
+	list_for_each_entry(hlink, &bus->hlink_list, list) {
+		if (!hlink->ref_count)
+			snd_hdac_ext_bus_link_power_down(hlink);
+	}
+
+	/* check dma status and clean up CORB/RIRB buffers */
+	if (!bus->cmd_dma_state)
+		snd_hdac_bus_stop_cmd_io(bus);
+
+	return 0;
+}
+
+static int __maybe_unused avs_suspend(struct device *dev)
+{
+	return avs_suspend_common(to_avs_dev(dev), true);
+}
+
+static int __maybe_unused avs_resume(struct device *dev)
+{
+	return avs_resume_common(to_avs_dev(dev), true, true);
+}
+
+static int __maybe_unused avs_runtime_suspend(struct device *dev)
+{
+	return avs_suspend_common(to_avs_dev(dev), true);
+}
+
+static int __maybe_unused avs_runtime_resume(struct device *dev)
+{
+	return avs_resume_common(to_avs_dev(dev), true, false);
+}
+
+static int __maybe_unused avs_freeze(struct device *dev)
+{
+	return avs_suspend_common(to_avs_dev(dev), false);
+}
+static int __maybe_unused avs_thaw(struct device *dev)
+{
+	return avs_resume_common(to_avs_dev(dev), false, true);
+}
+
+static int __maybe_unused avs_poweroff(struct device *dev)
+{
+	return avs_suspend_common(to_avs_dev(dev), false);
+}
+
+static int __maybe_unused avs_restore(struct device *dev)
+{
+	return avs_resume_common(to_avs_dev(dev), false, true);
+}
+
+static const struct dev_pm_ops avs_dev_pm = {
+	.suspend = avs_suspend,
+	.resume = avs_resume,
+	.freeze = avs_freeze,
+	.thaw = avs_thaw,
+	.poweroff = avs_poweroff,
+	.restore = avs_restore,
+	SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
+};
+
 static const struct pci_device_id avs_ids[] = {
 	{ 0 }
 };
@@ -546,6 +668,9 @@ static struct pci_driver avs_pci_driver = {
 	.id_table = avs_ids,
 	.probe = avs_pci_probe,
 	.remove = avs_pci_remove,
+	.driver = {
+		.pm = &avs_dev_pm,
+	},
 };
 module_pci_driver(avs_pci_driver);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 13/14] ASoC: Intel: avs: SKL-based platforms support
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (11 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 12/14] ASoC: Intel: avs: Power management Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-26 17:23 ` [PATCH 14/14] ASoC: Intel: avs: APL-based " Cezary Rojewski
  2022-04-27  8:15 ` [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Define handlers specific to cAVS 1.5 platforms, that is SKL, KBL, AML
and all other variants based on this very version of AudioDSP
architecture. Most are specific to SKL-alike platforms with only
skl_log_buffer_offset() being exposed and used later by younger
equivalents.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/Makefile    |   1 +
 sound/soc/intel/avs/avs.h       |   4 +
 sound/soc/intel/avs/core.c      |  18 +++++
 sound/soc/intel/avs/messages.h  |  18 +++++
 sound/soc/intel/avs/registers.h |   4 +
 sound/soc/intel/avs/skl.c       | 125 ++++++++++++++++++++++++++++++++
 6 files changed, 170 insertions(+)
 create mode 100644 sound/soc/intel/avs/skl.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 592d4dc02c56..7d09385bc970 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -3,6 +3,7 @@
 snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
 		    topology.o path.o pcm.o board_selection.o
 snd-soc-avs-objs += cldma.o
+snd-soc-avs-objs += skl.o
 
 snd-soc-avs-objs += trace.o
 # tell define_trace.h where to find the trace header
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 67a80d56c7ae..c0b8d6354089 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -56,6 +56,8 @@ struct avs_dsp_ops {
 #define avs_dsp_op(adev, op, ...) \
 	((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__))
 
+extern const struct avs_dsp_ops skl_dsp_ops;
+
 #define AVS_PLATATTR_CLDMA		BIT_ULL(0)
 #define AVS_PLATATTR_IMR		BIT_ULL(1)
 
@@ -249,6 +251,8 @@ 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);
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index c2f8fb87cfc2..b6387c2c6361 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -658,7 +658,25 @@ static const struct dev_pm_ops avs_dev_pm = {
 	SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
 };
 
+static const struct avs_spec skl_desc = {
+	.name = "skl",
+	.min_fw_version = {
+		.major = 9,
+		.minor = 21,
+		.hotfix = 0,
+		.build = 4732,
+	},
+	.dsp_ops = &skl_dsp_ops,
+	.core_init_mask = 1,
+	.attributes = AVS_PLATATTR_CLDMA,
+	.sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+	.sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
+	.rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
 static const struct pci_device_id avs_ids[] = {
+	{ PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */
+	{ PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */
 	{ 0 }
 };
 MODULE_DEVICE_TABLE(pci, avs_ids);
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 257482e160bc..981ec024b152 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -347,6 +347,24 @@ enum avs_log_enable {
 	AVS_LOG_ENABLE = 1
 };
 
+enum avs_skl_log_priority {
+	AVS_SKL_LOG_CRITICAL = 1,
+	AVS_SKL_LOG_HIGH,
+	AVS_SKL_LOG_MEDIUM,
+	AVS_SKL_LOG_LOW,
+	AVS_SKL_LOG_VERBOSE,
+};
+
+struct skl_log_state {
+	u32 enable;
+	u32 min_priority;
+} __packed;
+
+struct skl_log_state_info {
+	u32 core_mask;
+	struct skl_log_state logs_core[];
+} __packed;
+
 int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
 
 struct avs_fw_version {
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index b2100dc630e4..68f06aa4e10f 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -48,6 +48,10 @@
 #define SKL_ADSP_HIPCIE_DONE		BIT(30)
 #define SKL_ADSP_HIPCT_BUSY		BIT(31)
 
+/* Intel HD Audio SRAM windows base addresses */
+#define SKL_ADSP_SRAM_BASE_OFFSET	0x8000
+#define SKL_ADSP_SRAM_WINDOW_SIZE	0x2000
+
 /* Constants used when accessing SRAM, space shared with firmware */
 #define AVS_FW_REG_BASE(adev)		((adev)->spec->sram_base_offset)
 #define AVS_FW_REG_STATUS(adev)		(AVS_FW_REG_BASE(adev) + 0x0)
diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c
new file mode 100644
index 000000000000..bda5ec7510fe
--- /dev/null
+++ b/sound/soc/intel/avs/skl.c
@@ -0,0 +1,125 @@
+// 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 <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+
+static int skl_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 skl_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->core_mask = resource_mask;
+	if (enable)
+		for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) {
+			info->logs_core[i].enable = enable;
+			info->logs_core[i].min_priority = *priorities++;
+		}
+	else
+		for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0))
+			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;
+}
+
+int skl_log_buffer_offset(struct avs_dev *adev, u32 core)
+{
+	return core * avs_log_buffer_size(adev);
+}
+
+/* fw DbgLogWp registers */
+#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core)
+
+static int
+skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+	unsigned long flags;
+	void __iomem *buf;
+	u16 size, write, offset;
+
+	spin_lock_irqsave(&adev->dbg.trace_lock, flags);
+	if (!kfifo_initialized(&adev->dbg.trace_fifo)) {
+		spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+		return 0;
+	}
+
+	size = avs_log_buffer_size(adev) / 2;
+	write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core));
+	/* determine buffer half */
+	offset = (write < size) ? size : 0;
+
+	/* Address is guaranteed to exist in SRAM2. */
+	buf = avs_log_buffer_addr(adev, msg->log.core) + offset;
+	__kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock);
+	wake_up(&adev->dbg.trace_waitq);
+	spin_unlock_irqrestore(&adev->dbg.trace_lock, flags);
+
+	return 0;
+}
+
+static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg)
+{
+	u8 *dump;
+
+	dump = vzalloc(AVS_FW_REGS_SIZE);
+	if (!dump)
+		return -ENOMEM;
+
+	memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE);
+	dev_coredumpv(adev->dev, dump, AVS_FW_REGS_SIZE, GFP_KERNEL);
+
+	return 0;
+}
+
+static bool
+skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake)
+{
+	/* unsupported on cAVS 1.5 hw */
+	return false;
+}
+
+static int skl_set_d0ix(struct avs_dev *adev, bool enable)
+{
+	/* unsupported on cAVS 1.5 hw */
+	return 0;
+}
+
+const struct avs_dsp_ops skl_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_cldma_load_basefw,
+	.load_lib = avs_cldma_load_library,
+	.transfer_mods = avs_cldma_transfer_modules,
+	.enable_logs = skl_enable_logs,
+	.log_buffer_offset = skl_log_buffer_offset,
+	.log_buffer_status = skl_log_buffer_status,
+	.coredump = skl_coredump,
+	.d0ix_toggle = skl_d0ix_toggle,
+	.set_d0ix = skl_set_d0ix,
+};
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 14/14] ASoC: Intel: avs: APL-based platforms support
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (12 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 13/14] ASoC: Intel: avs: SKL-based platforms support Cezary Rojewski
@ 2022-04-26 17:23 ` Cezary Rojewski
  2022-04-27  8:15 ` [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-26 17:23 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: Cezary Rojewski, upstream, harshapriya.n, rad,
	pierre-louis.bossart, tiwai, hdegoede, amadeuszx.slawinski,
	cujomalainey, lma

Define handlers specific to cAVS 1.5+ platforms, that is, APL and
similar platforms. These differ from SKL-alike ones in terms of AudioDSP
firmware generation and thus the '+' suffix. Introduciton of IMR,
removal of CLDMA, D0IX support and monolithic-ation of library/module
code are most impactful but are not the only changes brought with this
newer generation. Some generic and 1.5 operations are being re-used to
reduce code size.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/soc/intel/avs/Makefile    |   2 +-
 sound/soc/intel/avs/apl.c       | 250 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/avs.h       |  13 ++
 sound/soc/intel/avs/core.c      |  18 +++
 sound/soc/intel/avs/loader.c    |   4 +
 sound/soc/intel/avs/messages.h  |   7 +
 sound/soc/intel/avs/registers.h |   2 +
 7 files changed, 295 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/apl.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 7d09385bc970..b6b93ae80304 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -3,7 +3,7 @@
 snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
 		    topology.o path.o pcm.o board_selection.o
 snd-soc-avs-objs += cldma.o
-snd-soc-avs-objs += skl.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
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 c0b8d6354089..e5f40b2f3d2e 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -57,6 +57,7 @@ struct avs_dsp_ops {
 	((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)
@@ -333,4 +334,16 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src,
 			 (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/core.c b/sound/soc/intel/avs/core.c
index b6387c2c6361..3442f73fca0c 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -674,9 +674,27 @@ static const struct avs_spec skl_desc = {
 	.rom_status = SKL_ADSP_SRAM_BASE_OFFSET,
 };
 
+static const struct avs_spec apl_desc = {
+	.name = "apl",
+	.min_fw_version = {
+		.major = 9,
+		.minor = 22,
+		.hotfix = 1,
+		.build = 4323,
+	},
+	.dsp_ops = &apl_dsp_ops,
+	.core_init_mask = 3,
+	.attributes = AVS_PLATATTR_IMR,
+	.sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET,
+	.sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE,
+	.rom_status = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
 static const struct pci_device_id avs_ids[] = {
 	{ PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */
 	{ PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */
+	{ PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */
+	{ PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */
 	{ 0 }
 };
 MODULE_DEVICE_TABLE(pci, avs_ids);
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index de98f4c3adf8..f6ceec3c229a 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -37,6 +37,8 @@
 #define AVS_EXT_MANIFEST_MAGIC		0x31454124
 #define SKL_MANIFEST_MAGIC		0x00000006
 #define SKL_ADSPFW_OFFSET		0x284
+#define APL_MANIFEST_MAGIC		0x44504324
+#define APL_ADSPFW_OFFSET		0x2000
 
 /* Occasionally, engineering (release candidate) firmware is provided for testing. */
 static bool debug_ignore_fw_version;
@@ -87,6 +89,8 @@ static int avs_fw_manifest_offset(struct firmware *fw)
 	switch (magic) {
 	case SKL_MANIFEST_MAGIC:
 		return SKL_ADSPFW_OFFSET;
+	case APL_MANIFEST_MAGIC:
+		return APL_ADSPFW_OFFSET;
 	default:
 		return -EINVAL;
 	}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 981ec024b152..c0f90dba9af8 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -365,6 +365,13 @@ struct skl_log_state_info {
 	struct skl_log_state logs_core[];
 } __packed;
 
+struct apl_log_state_info {
+	u32 aging_timer_period;
+	u32 fifo_full_timer_period;
+	u32 core_mask;
+	struct skl_log_state logs_core[];
+} __packed;
+
 int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size);
 
 struct avs_fw_version {
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 68f06aa4e10f..95be86148cf3 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -51,6 +51,8 @@
 /* Intel HD Audio SRAM windows base addresses */
 #define SKL_ADSP_SRAM_BASE_OFFSET	0x8000
 #define SKL_ADSP_SRAM_WINDOW_SIZE	0x2000
+#define APL_ADSP_SRAM_BASE_OFFSET	0x80000
+#define APL_ADSP_SRAM_WINDOW_SIZE	0x20000
 
 /* Constants used when accessing SRAM, space shared with firmware */
 #define AVS_FW_REG_BASE(adev)		((adev)->spec->sram_base_offset)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw
  2022-04-26 17:23 ` [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw Cezary Rojewski
@ 2022-04-26 21:21   ` Pierre-Louis Bossart
  2022-05-01  9:45     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:21 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



On 4/26/22 12:23, Cezary Rojewski wrote:
> Not all modules are part of base firmware. Some are part of loadable
> libraries. These need to be loaded after base firmware reports ready
> status through FW_READY notification.
> 
> Their loading process is similar to the base firmware's one. Request the
> binary file, verify and strip the manifest and load the actual code into
> DSP memory with help of CLDMA or HD-Audio render stream, depending on
> audio device generation.
> 
> List of libraries needed for loading is obtained through the topology -
> vendor sections specifying the name of firmware files to request.
> 
> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
> ---
>  sound/soc/intel/avs/avs.h    |  3 ++
>  sound/soc/intel/avs/loader.c | 79 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 82 insertions(+)
> 
> diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
> index c57a07a18d8e..14b4a780a91c 100644
> --- a/sound/soc/intel/avs/avs.h
> +++ b/sound/soc/intel/avs/avs.h
> @@ -19,6 +19,8 @@
>  
>  struct avs_dev;
>  struct avs_tplg;
> +struct avs_tplg_library;
> +struct avs_soc_component;
>  
>  /*
>   * struct avs_dsp_ops - Platform-specific DSP operations
> @@ -241,6 +243,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);
>  
> diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
> index c47f85161d95..de98f4c3adf8 100644
> --- a/sound/soc/intel/avs/loader.c
> +++ b/sound/soc/intel/avs/loader.c
> @@ -15,6 +15,7 @@
>  #include "cldma.h"
>  #include "messages.h"
>  #include "registers.h"
> +#include "topology.h"
>  
>  #define AVS_ROM_STS_MASK		0xFF
>  #define AVS_ROM_INIT_DONE		0x1
> @@ -466,6 +467,70 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
>  	return 0;
>  }
>  
> +int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
> +{
> +	int start, id, i = 0;
> +	int ret;
> +
> +	/* Calculate the id to assign for the next lib. */
> +	for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
> +		if (adev->lib_names[id][0] == '\0')
> +			break;
> +	if (id + num_libs >= adev->fw_cfg.max_libs_count)
> +		return -EINVAL;

use ida_alloc_max() ?

> +
> +	start = id;
> +	while (i < num_libs) {
> +		struct avs_fw_manifest *man;
> +		const struct firmware *fw;
> +		struct firmware stripped_fw;
> +		char *filename;
> +		int j;
> +
> +		filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
> +				     libs[i].name);
> +		if (!filename)
> +			return -ENOMEM;
> +
> +		ret = avs_request_firmware(adev, &fw, filename);
> +		kfree(filename);
> +		if (ret < 0)
> +			return ret;
> +
> +		stripped_fw = *fw;
> +		ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
> +		if (ret) {
> +			dev_err(adev->dev, "invalid library data: %d\n", ret);
> +			goto release_fw;
> +		}
> +
> +		ret = avs_fw_manifest_offset(&stripped_fw);
> +		if (ret < 0)
> +			goto release_fw;
> +		man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
> +
> +		/* Don't load anything that's already in DSP memory. */
> +		for (j = 0; j < id; j++)
> +			if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
> +				goto next_lib;
> +
> +		ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
> +		if (ret)
> +			goto release_fw;
> +
> +		strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
> +		id++;
> +next_lib:
> +		i++;
> +	}
> +
> +	return start == id ? 1 : 0;
> +
> +release_fw:
> +	avs_release_last_firmware(adev);

why release only the last library and not all the ones that were previous loaded?
or why bother to even release the last since at this point you probably need to restart completely?

> +	return ret;
> +}
> +
>  static int avs_dsp_load_basefw(struct avs_dev *adev)
>  {
>  	const struct avs_fw_version *min_req;
> @@ -519,6 +584,7 @@ static int avs_dsp_load_basefw(struct avs_dev *adev)
>  
>  int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
>  {
> +	struct avs_soc_component *acomp;
>  	int ret, i;
>  
>  	/* Forgo full boot if flash from IMR succeeds. */
> @@ -538,7 +604,20 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
>  	avs_hda_l1sen_enable(adev, false);
>  
>  	ret = avs_dsp_load_basefw(adev);
> +	if (ret)
> +		goto reenable_gating;
> +
> +	mutex_lock(&adev->comp_list_mutex);
> +	list_for_each_entry(acomp, &adev->comp_list, node) {
> +		struct avs_tplg *tplg = acomp->tplg;
> +
> +		ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
> +		if (ret < 0)
> +			break;
> +	}
> +	mutex_unlock(&adev->comp_list_mutex);
>  
> +reenable_gating:
>  	avs_hda_l1sen_enable(adev, true);
>  	avs_hda_clock_gating_enable(adev, true);
>  

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver
  2022-04-26 17:23 ` [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver Cezary Rojewski
@ 2022-04-26 21:33   ` Pierre-Louis Bossart
  2022-05-01 10:45     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:33 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



> +struct avs_dma_data {
> +	struct avs_tplg_path_template *template;
> +	struct avs_path *path;
> +	/*
> +	 * link stream is stored within substream's runtime
> +	 * private_data to fulfill the needs of codec BE path
> +	 *
> +	 * host stream assigned

not able to parse that comment, what are you trying to say?

> +	 */
> +	struct hdac_ext_stream *host_stream;
> +};
> +
> +static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
> +				  loff_t *ppos)
> +{
> +	struct snd_soc_component *component = file->private_data;
> +	struct snd_soc_card *card = component->card;
> +	struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
> +	char buf[64];
> +	size_t len;
> +
> +	len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
> +		       mach->tplg_filename);
> +
> +	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
> +}
> +
> +static const struct file_operations topology_name_fops = {
> +	.open = simple_open,
> +	.read = topology_name_read,
> +	.llseek = default_llseek,
> +};

can you clarify why this is needed?

> +
> +static int avs_component_load_libraries(struct avs_soc_component *acomp)
> +{
> +	struct avs_tplg *tplg = acomp->tplg;
> +	struct avs_dev *adev = to_avs_dev(acomp->base.dev);
> +	int ret;
> +
> +	if (!tplg->num_libs)
> +		return 0;
> +
> +	/* Parent device may be asleep and library loading involves IPCs. */
> +	ret = pm_runtime_get_sync(adev->dev);
> +	if (ret < 0 && ret != -EACCES) {
> +		pm_runtime_put_noidle(adev->dev);
> +		return ret;
> +	}

Mark recommends the use of pm_runtime_resume_and_get(), see patches from today.

> +
> +	avs_hda_clock_gating_enable(adev, false);
> +	avs_hda_l1sen_enable(adev, false);
> +
> +	ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
> +
> +	avs_hda_l1sen_enable(adev, true);
> +	avs_hda_clock_gating_enable(adev, true);
> +
> +	if (!ret)
> +		ret = avs_module_info_init(adev, false);
> +
> +	pm_runtime_mark_last_busy(adev->dev);
> +	pm_runtime_put_autosuspend(adev->dev);
> +
> +	return ret;
> +}
> +
> +static int avs_component_probe(struct snd_soc_component *component)
> +{
> +	struct snd_soc_card *card = component->card;
> +	struct snd_soc_acpi_mach *mach;
> +	struct avs_soc_component *acomp;
> +	struct avs_dev *adev;
> +	char *filename;
> +	int ret;
> +
> +	dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
> +	mach = dev_get_platdata(card->dev);
> +	acomp = to_avs_soc_component(component);
> +	adev = to_avs_dev(component->dev);
> +
> +	acomp->tplg = avs_tplg_new(component);
> +	if (!acomp->tplg)
> +		return -ENOMEM;
> +
> +	if (!mach->tplg_filename)
> +		goto finalize;
> +
> +	/* Load specified topology and create sysfs for it. */
> +	filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
> +			     mach->tplg_filename);

what is the link between topology and sysfs?

> +	if (!filename)
> +		return -ENOMEM;
> +
> +	ret = avs_load_topology(component, filename);
> +	kfree(filename);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = avs_component_load_libraries(acomp);
> +	if (ret < 0) {
> +		dev_err(card->dev, "libraries loading failed: %d\n", ret);
> +		goto err_load_libs;
> +	}
> +
> +finalize:
> +	debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
> +			    &topology_name_fops);

that's debugfs here, is this to make it possible to select an alternate topology file? If yes, a comment earlier wouldn't hurt.

> +
> +	mutex_lock(&adev->comp_list_mutex);
> +	list_add_tail(&acomp->node, &adev->comp_list);
> +	mutex_unlock(&adev->comp_list_mutex);
> +
> +	return 0;
> +
> +err_load_libs:
> +	avs_remove_topology(component);
> +	return ret;
> +}
> +


> +static const struct snd_soc_component_driver avs_component_driver = {
> +	.name			= "avs-pcm",
> +	.probe			= avs_component_probe,
> +	.remove			= avs_component_remove,
> +	.open			= avs_component_open,
> +	.pointer		= avs_component_pointer,
> +	.mmap			= avs_component_mmap,
> +	.pcm_construct		= avs_component_construct,
> +	.module_get_upon_open	= 1, /* increment refcount when a pcm is opened */
> +	.topology_name_prefix	= "intel/avs",

is this intentional that the firmware binaries and topologies will be stored in the same intel/avs directory?

> +	.non_legacy_dai_naming	= true,

is this needed? we've never used this for Intel?

> +};
> +

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations
  2022-04-26 17:23 ` [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations Cezary Rojewski
@ 2022-04-26 21:40   ` Pierre-Louis Bossart
  2022-05-01 10:48     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:40 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma


> +static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
> +				     struct snd_soc_dai *dai)
> +{
> +	struct avs_dma_data *data;
> +	int ret = 0;
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
> +		if (ret < 0)
> +			dev_err(dai->dev, "run BE path failed: %d\n", ret);
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		ret = avs_path_pause(data->path);
> +		if (ret < 0)
> +			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
> +
> +		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
> +			ret = avs_path_reset(data->path);
> +			if (ret < 0)
> +				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
> +		}
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;

TRIGGER_SUSPEND will result in -EINVAL?

Have you tried suspend-resume while playing audio?

> +	}
> +
> +	return ret;
> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 05/14] ASoC: Intel: avs: HDA PCM BE operations
  2022-04-26 17:23 ` [PATCH 05/14] ASoC: Intel: avs: HDA " Cezary Rojewski
@ 2022-04-26 21:45   ` Pierre-Louis Bossart
  2022-05-01 10:55     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:45 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



> +static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct avs_dma_data *data;
> +	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
> +	struct hdac_ext_stream *estream;

host_stream, estream, there seems to be multiple naming conventions for the same thing?

> +	struct hdac_ext_link *link;
> +	struct hda_codec *codec;
> +
> +	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!data->path)
> +		return 0;
> +
> +	estream = substream->runtime->private_data;
> +	estream->link_prepared = false;
> +	avs_path_free(data->path);
> +	data->path = NULL;
> +
> +	/* clear link <-> stream mapping */
> +	codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
> +	link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
> +	if (!link)
> +		return -EINVAL;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		snd_hdac_ext_link_clear_stream_id(link, estream->hstream.stream_tag);
> +
> +	return 0;
> +}

> +static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct hdac_ext_stream *estream;
> +	struct avs_dma_data *data;
> +	int ret = 0;
> +
> +	dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
> +
> +	data = snd_soc_dai_get_dma_data(dai, substream);
> +	estream = substream->runtime->private_data;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		snd_hdac_ext_link_stream_start(estream);
> +
> +		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
> +		if (ret < 0)
> +			dev_err(dai->dev, "run BE path failed: %d\n", ret);
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		ret = avs_path_pause(data->path);
> +		if (ret < 0)
> +			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
> +
> +		snd_hdac_ext_link_stream_clear(estream);
> +
> +		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
> +			ret = avs_path_reset(data->path);
> +			if (ret < 0)
> +				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
> +		}
> +		break;
> +
> +	default:
> +		ret = -EINVAL;

TRIGGER_SUSPEND?

> +		break;
> +	}
> +
> +	return ret;
> +}

> +static const struct snd_soc_component_driver avs_hda_component_driver = {
> +	.name			= "avs-hda-pcm",
> +	.probe			= avs_component_hda_probe,
> +	.remove			= avs_component_hda_remove,
> +	.open			= avs_component_hda_open,
> +	.close			= avs_component_hda_close,
> +	.pointer		= avs_component_pointer,
> +	.mmap			= avs_component_mmap,
> +	.pcm_construct		= avs_component_construct,
> +	/*
> +	 * hda platform component's probe() is dependent on
> +	 * codec->pcm_list_head, it needs to be initialized after codec
> +	 * component. remove_order is here for completeness sake
> +	 */
> +	.probe_order		= SND_SOC_COMP_ORDER_LATE,
> +	.remove_order		= SND_SOC_COMP_ORDER_EARLY,
> +	.module_get_upon_open	= 1,
> +	.topology_name_prefix	= "intel/avs",
> +	.non_legacy_dai_naming	= true,

needed?

> +};
> +
> +int avs_hda_platform_register(struct avs_dev *adev, const char *name)
> +{
> +	return avs_soc_component_register(adev->dev, name,
> +					  &avs_hda_component_driver, NULL, 0);
> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow
  2022-04-26 17:23 ` [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow Cezary Rojewski
@ 2022-04-26 21:53   ` Pierre-Louis Bossart
  2022-05-01 15:32     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:53 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



On 4/26/22 12:23, Cezary Rojewski wrote:
> In rare occassions, under stress conditions or hardware malfunction, DSP

occasions

> firmware may fail. Software is notified about such situation with
> EXCEPTION_CAUGHT notification. IPC timeout is also counted as critical
> device failure. More often than not, driver can recover from such
> situations by performing full reset: killing and restarting ADSP.
> 
> Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
> ---
>  sound/soc/intel/Kconfig        |  1 +
>  sound/soc/intel/avs/avs.h      |  4 ++
>  sound/soc/intel/avs/ipc.c      | 95 +++++++++++++++++++++++++++++++++-
>  sound/soc/intel/avs/messages.h |  5 ++
>  4 files changed, 103 insertions(+), 2 deletions(-)
> 
> diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
> index c364ddf22267..05ad6bdecfc5 100644
> --- a/sound/soc/intel/Kconfig
> +++ b/sound/soc/intel/Kconfig
> @@ -218,6 +218,7 @@ config SND_SOC_INTEL_AVS
>  	select SND_HDA_EXT_CORE
>  	select SND_HDA_DSP_LOADER
>  	select SND_INTEL_NHLT
> +	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/avs.h b/sound/soc/intel/avs/avs.h
> index e628f78d1864..02c2aa1bcd5c 100644
> --- a/sound/soc/intel/avs/avs.h
> +++ b/sound/soc/intel/avs/avs.h
> @@ -42,6 +42,7 @@ 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 coredump)(struct avs_dev *, union avs_notify_msg *);
>  };
>  
>  #define avs_dsp_op(adev, op, ...) \
> @@ -164,12 +165,15 @@ struct avs_ipc {
>  	struct avs_ipc_msg rx;
>  	u32 default_timeout_ms;
>  	bool ready;
> +	bool 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;
>  };
>  
>  #define AVS_EIPC	EREMOTEIO
> diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
> index 68aaf01edbf2..84cb411c82fa 100644
> --- a/sound/soc/intel/avs/ipc.c
> +++ b/sound/soc/intel/avs/ipc.c
> @@ -14,6 +14,87 @@
>  
>  #define AVS_IPC_TIMEOUT_MS	300
>  
> +static void avs_dsp_recovery(struct avs_dev *adev)
> +{
> +	struct avs_soc_component *acomp;
> +	unsigned int core_mask;
> +	int ret;
> +
> +	if (adev->ipc->recovering)
> +		return;
> +	adev->ipc->recovering = true;

don't you need some sort of lock to test/clear this flag?

> +
> +	mutex_lock(&adev->comp_list_mutex);
> +	/* disconnect all running streams */
> +	list_for_each_entry(acomp, &adev->comp_list, node) {
> +		struct snd_soc_pcm_runtime *rtd;
> +		struct snd_soc_card *card;
> +
> +		card = acomp->base.card;
> +		if (!card)
> +			continue;
> +
> +		for_each_card_rtds(card, rtd) {
> +			struct snd_pcm *pcm;
> +			int dir;
> +
> +			pcm = rtd->pcm;
> +			if (!pcm || rtd->dai_link->no_pcm)
> +				continue;
> +
> +			for_each_pcm_streams(dir) {
> +				struct snd_pcm_substream *substream;
> +
> +				substream = pcm->streams[dir].substream;
> +				if (!substream || !substream->runtime)
> +					continue;
> +
> +				snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
> +			}
> +		}
> +	}
> +	mutex_unlock(&adev->comp_list_mutex);
> +
> +	/* forcibly shutdown all cores */
> +	core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
> +	avs_dsp_core_disable(adev, core_mask);
> +
> +	/* attempt dsp reboot */
> +	ret = avs_dsp_boot_firmware(adev, true);
> +	if (ret < 0)
> +		dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
> +
> +	pm_runtime_mark_last_busy(adev->dev);
> +	pm_runtime_enable(adev->dev);
> +	pm_request_autosuspend(adev->dev);

there are zero users of this routine in the entire sound/ tree, can you clarify why this is needed or what you are trying to do?

> +
> +	adev->ipc->recovering = false;
> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 08/14] ASoC: Intel: avs: D0ix power state support
  2022-04-26 17:23 ` [PATCH 08/14] ASoC: Intel: avs: D0ix power state support Cezary Rojewski
@ 2022-04-26 21:58   ` Pierre-Louis Bossart
  2022-04-29 14:19     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 21:58 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



On 4/26/22 12:23, Cezary Rojewski wrote:
> Audio DSP device supports D0 substates in form of D0ix, allowing for
> preserving more power even when device is still considered active (D0).
> When entered, certain domains which are not being currently used become
> power gated. Entering and leaving D0ix is a complex process and differs
> between firmware generations.
> 
> Conditions that disallow D0i3 and require immediate D0i0 transition
> include but may not be limited to: IPC traffic, firmware tracing and
> SRAM I/O. To make D0ix toggling sane, delay D0i3 transition and refresh
> the timer each time an IPC is requrested.

typo: requested.

I find it odd to list all kinds of criteria but only handle one in the end. Do the other matter, is this a TODO, unclear what you are trying to say.

>  int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
> diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
> index 3ff17bd22a5a..2f18b137ff42 100644
> --- a/sound/soc/intel/avs/dsp.c
> +++ b/sound/soc/intel/avs/dsp.c
> @@ -152,6 +152,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
>  
>  	adev->core_refs[core_id]++;
>  	if (adev->core_refs[core_id] == 1) {
> +		/*
> +		 * No cores other than main-core can be running for DSP
> +		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,

conscious failure? what's that?

> +		 * simply d0ix power state will no longer be attempted.
> +		 */
> +		ret = avs_dsp_disable_d0ix(adev);
> +		if (ret && ret != -AVS_EIPC)
> +			goto err_disable_d0ix;
> +
>  		ret = avs_dsp_enable(adev, mask);
>  		if (ret)
>  			goto err_enable_dsp;
tatic int
> +avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +	int ret;
> +
> +	/* Is transition required? */
> +	if (ipc->in_d0ix == enable)
> +		return 0;
> +
> +	ret = avs_dsp_op(adev, set_d0ix, enable);
> +	if (ret) {
> +		/* Prevent further d0ix attempts on conscious IPC failure. */

??

> +		if (ret == -AVS_EIPC)
> +			atomic_inc(&ipc->d0ix_disable_depth);
> +
> +		ipc->in_d0ix = false;
> +		return ret;
> +	}
> +
> +	ipc->in_d0ix = enable;
> +	return 0;
> +}
> +
> +static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx)
> +{
> +	if (atomic_read(&adev->ipc->d0ix_disable_depth))
> +		return;
> +
> +	mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work,
> +			 msecs_to_jiffies(AVS_D0IX_DELAY_MS));
> +}
> +
> +static void avs_dsp_d0ix_work(struct work_struct *work)
> +{
> +	struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work);
> +
> +	avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true);
> +}
> +
> +static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +
> +	if (!atomic_read(&ipc->d0ix_disable_depth)) {
> +		cancel_delayed_work_sync(&ipc->d0ix_work);
> +		return avs_dsp_set_d0ix(adev, false);
> +	}
> +
> +	return 0;
> +}
> +
> +int avs_dsp_disable_d0ix(struct avs_dev *adev)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +
> +	/* Prevent PG only on the first disable. */
> +	if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) {
> +		cancel_delayed_work_sync(&ipc->d0ix_work);
> +		return avs_dsp_set_d0ix(adev, false);
> +	}
> +
> +	return 0;
> +}
> +
> +int avs_dsp_enable_d0ix(struct avs_dev *adev)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +
> +	if (atomic_dec_and_test(&ipc->d0ix_disable_depth))
> +		queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work,
> +				   msecs_to_jiffies(AVS_D0IX_DELAY_MS));
> +	return 0;
> +}
>  
>  static void avs_dsp_recovery(struct avs_dev *adev)
>  {
> @@ -391,10 +467,35 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
>  	return ret;
>  }
>  
> +static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request,
> +				     struct avs_ipc_msg *reply, int timeout, bool wake_d0i0,
> +				     bool schedule_d0ix)
> +{
> +	int ret;
> +
> +	if (wake_d0i0) {
> +		ret = avs_dsp_wake_d0i0(adev, request);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = avs_dsp_do_send_msg(adev, request, reply, timeout);
> +	if (ret)
> +		return ret;
> +
> +	if (schedule_d0ix)
> +		avs_dsp_schedule_d0ix(adev, request);
> +
> +	return 0;
> +}
> +
>  int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
>  			     struct avs_ipc_msg *reply, int timeout)
>  {
> -	return avs_dsp_do_send_msg(adev, request, reply, timeout);
> +	bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true);
> +	bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false);
> +
> +	return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix);
>  }
>  
>  int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
> @@ -403,6 +504,19 @@ int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
>  	return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms);
>  }
>  
> +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)
> +{
> +	return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false);
> +}
> +
> +int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
> +			struct avs_ipc_msg *reply, bool wake_d0i0)
> +{
> +	return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms,
> +					   wake_d0i0);
> +}
> +
>  static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout)
>  {
>  	struct avs_ipc *ipc = adev->ipc;
> @@ -463,6 +577,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
>  	ipc->ready = false;
>  	ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
>  	INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work);
> +	INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work);
>  	init_completion(&ipc->done_completion);
>  	init_completion(&ipc->busy_completion);
>  	spin_lock_init(&ipc->rx_lock);
> @@ -475,4 +590,6 @@ void avs_ipc_block(struct avs_ipc *ipc)
>  {
>  	ipc->ready = false;
>  	cancel_work_sync(&ipc->recovery_work);
> +	cancel_delayed_work_sync(&ipc->d0ix_work);
> +	ipc->in_d0ix = false;
>  }
> diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
> index 3da33150aabf..6404fce8cde4 100644
> --- a/sound/soc/intel/avs/messages.c
> +++ b/sound/soc/intel/avs/messages.c
> @@ -432,7 +432,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
>  	request.data = &dx;
>  	request.size = sizeof(dx);
>  
> -	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
>  	if (ret)
>  		avs_ipc_err(adev, &request, "set dx", ret);
>  
> @@ -456,7 +456,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
>  
>  	request.header = msg.val;
>  
> -	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
>  	if (ret)
>  		avs_ipc_err(adev, &request, "set d0ix", ret);
>  

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-04-26 17:23 ` [PATCH 10/14] ASoC: Intel: avs: Machine board registration Cezary Rojewski
@ 2022-04-26 22:12   ` Pierre-Louis Bossart
  2022-04-29 14:01     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 22:12 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



On 4/26/22 12:23, Cezary Rojewski wrote:
> AVS driver operates with granular audio card division in mind.
> Super-card approach (e.g.: I2S, DMIC and HDA DAIs combined) is
> deprecated in favour of individual cards - one per each device. This
> provides necessary dynamism, especially for configurations with number
> of codecs present and makes it easier to survive auxiliary devices
> failures - one card failing to probe does not prevent others from
> succeeding.
> 
> All boards spawned by AVS are unregistered on ->remove(). This includes
> dummy codecs such as DMIC.
> 
> As all machine boards found in sound/soc/intel/boards are irreversibly
> tied to 'super-card' approach, new boards are going to be introduced.
> This temporarily increases number of boards available under /intel
> directory until skylake-driver becomes deprecated and removed.

I thought you wanted your own directory for cards, what's the point of adding new machine drivers in intel/boards if they ONLY work with your AVS driver?

Also you can only remove the machine drivers that are NOT shared with SOF...


> +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",
> +		.link_mask = AVS_SSP(0),

I've told this before, 'link_mask' was introduced for *SoundWire*. Please do not overload existing concepts and use this instead:

@i2s_link_mask: I2S/TDM links enabled on the board

> +		.tplg_filename = "skl-rt286-tplg.bin",
> +	},
> +	{
> +		.id = "10508825",
> +		.drv_name = "avs_nau8825",
> +		.link_mask = AVS_SSP(1),
> +		.tplg_filename = "skl-nau8825-tplg.bin",
> +	},
> +	{
> +		.id = "INT343B",
> +		.drv_name = "avs_ssm4567",
> +		.link_mask = AVS_SSP(0),
> +		.tplg_filename = "skl-ssm4567-tplg.bin",
> +	},
> +	{
> +		.id = "MX98357A",
> +		.drv_name = "avs_max98357a",
> +		.link_mask = AVS_SSP(0),
> +		.tplg_filename = "skl-max98357a-tplg.bin",
> +	},
> +	{},
> +};
> +
> +static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
> +	{
> +		.id = "INT343A",
> +		.drv_name = "avs_rt286",
> +		.link_mask = AVS_SSP(0),
> +		.quirk_data = &kbl_dmi_table,
> +		.machine_quirk = dmi_match_quirk,
> +		.tplg_filename = "kbl-rt286-tplg.bin",
> +	},
> +	{
> +		.id = "INT343A",
> +		.drv_name = "avs_rt298",
> +		.link_mask = AVS_SSP(0),
> +		.quirk_data = &kbl_r_dmi_table,
> +		.machine_quirk = dmi_match_quirk,
> +		.tplg_filename = "kblr-rt298-tplg.bin",
> +	},
> +	{
> +		.id = "MX98373",
> +		.drv_name = "avs_max98373",
> +		.link_mask = AVS_SSP(0),
> +		.tplg_filename = "kbl-max98373-tplg.bin",
> +	},
> +	{
> +		.id = "DLGS7219",
> +		.drv_name = "avs_da7219",
> +		.link_mask = AVS_SSP(1),
> +		.tplg_filename = "kbl-da7219-tplg.bin",
> +	},
> +	{},
> +};
> +
> +static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
> +	{
> +		.id = "INT343A",
> +		.drv_name = "avs_rt298",
> +		.link_mask = AVS_SSP(5),
> +		.tplg_filename = "apl-rt298-tplg.bin",
> +	},
> +	{
> +		.id = "INT34C3",
> +		.drv_name = "avs_tdf8532",
> +		.link_mask = AVS_SSP_RANGE(0, 5),
> +		.pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */
> +		.tplg_filename = "apl-tdf8532-tplg.bin",
> +	},
> +	{
> +		.id = "MX98357A",
> +		.drv_name = "avs_max98357a",
> +		.link_mask = AVS_SSP(5),
> +		.tplg_filename = "apl-max98357a-tplg.bin",
> +	},
> +	{
> +		.id = "DLGS7219",
> +		.drv_name = "avs_da7219",
> +		.link_mask = AVS_SSP(1),
> +		.tplg_filename = "apl-da7219-tplg.bin",
> +	},
> +	{},
> +};
> +
> +static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = {
> +	{
> +		.id = "INT343A",
> +		.drv_name = "avs_rt298",
> +		.link_mask = AVS_SSP(2),
> +		.tplg_filename = "gml-rt298-tplg.bin",
> +	},
> +	{},
> +};
> +
> +static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(0),
> +		.tplg_filename = "avs_ssp_test.bin",
> +	},
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(1),
> +		.tplg_filename = "avs_ssp_test.bin",
> +	},
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(2),
> +		.tplg_filename = "avs_ssp_test.bin",
> +	},
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(3),
> +		.tplg_filename = "avs_ssp_test.bin",
> +	},
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(4),
> +		.tplg_filename = "avs_ssp_test.bin",
> +	},
> +	{
> +		.drv_name = "avs_ssp_test",
> +		.link_mask = AVS_SSP(5),
> +		.tplg_filename = "avs_ssp_test.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 */
> +	{},

you are not using the intel/commmon matching and ACPI tables so I would recommend you deal with machine drivers in your private space.


> +static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
> +{
> +	struct snd_soc_acpi_mach mach = {{0}};
> +	struct platform_device *board;
> +	struct hdac_device *hdev = &codec->core;
> +	char *pname;
> +	int ret, id;
> +
> +	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
> +	if (!pname)
> +		return -ENOMEM;
> +
> +	ret = avs_hda_platform_register(adev, pname);
> +	if (ret < 0)
> +		return ret;
> +
> +	mach.pdata = codec;
> +	mach.mach_params.platform = pname;
> +	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
> +					    hdev->vendor_id);

this is surprising, how many topologies will you end-up supporting then? Topologies are typically NOT dependent on the HDaudio codec type or vendor and only deal with HDaudio link DMA configurations.

> +	if (!mach.tplg_filename)
> +		return -ENOMEM;
> +
> +	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
> +	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
> +					      sizeof(mach));
> +	if (IS_ERR(board)) {
> +		dev_err(adev->dev, "hda 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;
> +}
> +

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/14] ASoC: Intel: avs: Power management
  2022-04-26 17:23 ` [PATCH 12/14] ASoC: Intel: avs: Power management Cezary Rojewski
@ 2022-04-26 22:18   ` Pierre-Louis Bossart
  2022-04-29 13:44     ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-04-26 22:18 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma



On 4/26/22 12:23, Cezary Rojewski wrote:
> To preserve power during sleep operations, handle suspend (S3),
> hibernation (S4) and runtime (RTD3) transitions. As flow for all of
> is shared, define common handlers to reduce code size.
> 
> Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
> ---
>  sound/soc/intel/avs/core.c | 125 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 125 insertions(+)
> 
> diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
> index 93180c22032d..c2f8fb87cfc2 100644
> --- a/sound/soc/intel/avs/core.c
> +++ b/sound/soc/intel/avs/core.c
> @@ -536,6 +536,128 @@ static void avs_pci_remove(struct pci_dev *pci)
>  	pm_runtime_get_noresume(&pci->dev);
>  }
>  
> +static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power)

low_power is not used so....

> +{
> +	struct hdac_bus *bus = &adev->base.core;
> +	int ret;
> +
> +	flush_work(&adev->probe_work);
> +
> +	snd_hdac_ext_bus_link_power_down_all(bus);
> +
> +	ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
> +	/*
> +	 * pm_runtime is blocked on DSP failure but system-wide suspend is not.
> +	 * Do not block entire system from suspending if that's the case.
> +	 */
> +	if (ret && ret != -EPERM) {
> +		dev_err(adev->dev, "set dx failed: %d\n", ret);
> +		return AVS_IPC_RET(ret);
> +	}
> +
> +	avs_dsp_op(adev, int_control, false);
> +	snd_hdac_ext_bus_ppcap_int_enable(bus, false);
> +
> +	ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
> +		return ret;
> +	}
> +
> +	snd_hdac_ext_bus_ppcap_enable(bus, false);
> +	/* disable LP SRAM retention */
> +	avs_hda_power_gating_enable(adev, false);
> +	snd_hdac_bus_stop_chip(bus);
> +	/* disable CG when putting controller to reset */
> +	avs_hdac_clock_gating_enable(bus, false);
> +	snd_hdac_bus_enter_link_reset(bus);
> +	avs_hdac_clock_gating_enable(bus, true);
> +
> +	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
> +{
> +	struct hdac_bus *bus = &adev->base.core;
> +	struct hdac_ext_link *hlink;
> +	int ret;
> +
> +	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
> +	avs_hdac_bus_init_chip(bus, true);
> +
> +	snd_hdac_ext_bus_ppcap_enable(bus, true);
> +	snd_hdac_ext_bus_ppcap_int_enable(bus, true);
> +
> +	ret = avs_dsp_boot_firmware(adev, purge);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "firmware boot failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* turn off the links that were off before suspend */
> +	list_for_each_entry(hlink, &bus->hlink_list, list) {
> +		if (!hlink->ref_count)
> +			snd_hdac_ext_bus_link_power_down(hlink);
> +	}
> +
> +	/* check dma status and clean up CORB/RIRB buffers */
> +	if (!bus->cmd_dma_state)
> +		snd_hdac_bus_stop_cmd_io(bus);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused avs_suspend(struct device *dev)
> +{
> +	return avs_suspend_common(to_avs_dev(dev), true);
> +}
> +
> +static int __maybe_unused avs_resume(struct device *dev)
> +{
> +	return avs_resume_common(to_avs_dev(dev), true, true);
> +}
> +
> +static int __maybe_unused avs_runtime_suspend(struct device *dev)
> +{
> +	return avs_suspend_common(to_avs_dev(dev), true);
> +}
> +
> +static int __maybe_unused avs_runtime_resume(struct device *dev)
> +{
> +	return avs_resume_common(to_avs_dev(dev), true, false);
> +}
> +
> +static int __maybe_unused avs_freeze(struct device *dev)
> +{
> +	return avs_suspend_common(to_avs_dev(dev), false);
> +}
> +static int __maybe_unused avs_thaw(struct device *dev)
> +{
> +	return avs_resume_common(to_avs_dev(dev), false, true);
> +}
> +
> +static int __maybe_unused avs_poweroff(struct device *dev)
> +{
> +	return avs_suspend_common(to_avs_dev(dev), false);
> +}
> +
> +static int __maybe_unused avs_restore(struct device *dev)
> +{
> +	return avs_resume_common(to_avs_dev(dev), false, true);
> +}
> +
> +static const struct dev_pm_ops avs_dev_pm = {
> +	.suspend = avs_suspend,
> +	.resume = avs_resume,

... all the 4 functions below seem unnecessary?

> +	.freeze = avs_freeze,
> +	.thaw = avs_thaw,
> +	.poweroff = avs_poweroff,
> +	.restore = avs_restore,

we've historically never used those, what drives this new usage?

on the SOF side, we've used prepare and complete, along with idle...


> +	SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
> +};
> +
>  static const struct pci_device_id avs_ids[] = {
>  	{ 0 }
>  };
> @@ -546,6 +668,9 @@ static struct pci_driver avs_pci_driver = {
>  	.id_table = avs_ids,
>  	.probe = avs_pci_probe,
>  	.remove = avs_pci_remove,
> +	.driver = {
> +		.pm = &avs_dev_pm,
> +	},
>  };
>  module_pci_driver(avs_pci_driver);
>  

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations
  2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
                   ` (13 preceding siblings ...)
  2022-04-26 17:23 ` [PATCH 14/14] ASoC: Intel: avs: APL-based " Cezary Rojewski
@ 2022-04-27  8:15 ` Cezary Rojewski
  14 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-27  8:15 UTC (permalink / raw)
  To: alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, pierre-louis.bossart, tiwai,
	hdegoede, amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 7:23 PM, Cezary Rojewski wrote:
> 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.


I'm aware that series generates conflicts due to recent changes in 
sound/soc/intel/Kconfig but I believe it's better to leave as is, let 
people provide comments without sending immediate v2 just for the sake 
of it building.

Given that we already received good feedback from Pierre the decision 
seems to be the right one. Fixes addressing the conflict will be part of 
v2 along with all other necessary changes given the review.


Regards,
Czarek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/14] ASoC: Intel: avs: Power management
  2022-04-26 22:18   ` Pierre-Louis Bossart
@ 2022-04-29 13:44     ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-29 13:44 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-27 12:18 AM, Pierre-Louis Bossart wrote:
> On 4/26/22 12:23, Cezary Rojewski wrote:
>> To preserve power during sleep operations, handle suspend (S3),
>> hibernation (S4) and runtime (RTD3) transitions. As flow for all of
>> is shared, define common handlers to reduce code size.
>>
>> Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
>> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
>> ---
>>   sound/soc/intel/avs/core.c | 125 +++++++++++++++++++++++++++++++++++++
>>   1 file changed, 125 insertions(+)
>>
>> diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
>> index 93180c22032d..c2f8fb87cfc2 100644
>> --- a/sound/soc/intel/avs/core.c
>> +++ b/sound/soc/intel/avs/core.c
>> @@ -536,6 +536,128 @@ static void avs_pci_remove(struct pci_dev *pci)
>>   	pm_runtime_get_noresume(&pci->dev);
>>   }
>>   
>> +static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power)
> 
> low_power is not used so....


Indeed, this argument should be part of the patch which introduces its 
usage. Removing in v2!

>> +{
>> +	struct hdac_bus *bus = &adev->base.core;
>> +	int ret;
>> +
>> +	flush_work(&adev->probe_work);
>> +
>> +	snd_hdac_ext_bus_link_power_down_all(bus);
>> +
>> +	ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false);
>> +	/*
>> +	 * pm_runtime is blocked on DSP failure but system-wide suspend is not.
>> +	 * Do not block entire system from suspending if that's the case.
>> +	 */
>> +	if (ret && ret != -EPERM) {
>> +		dev_err(adev->dev, "set dx failed: %d\n", ret);
>> +		return AVS_IPC_RET(ret);
>> +	}
>> +
>> +	avs_dsp_op(adev, int_control, false);
>> +	snd_hdac_ext_bus_ppcap_int_enable(bus, false);
>> +
>> +	ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
>> +	if (ret < 0) {
>> +		dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret);
>> +		return ret;
>> +	}
>> +
>> +	snd_hdac_ext_bus_ppcap_enable(bus, false);
>> +	/* disable LP SRAM retention */
>> +	avs_hda_power_gating_enable(adev, false);
>> +	snd_hdac_bus_stop_chip(bus);
>> +	/* disable CG when putting controller to reset */
>> +	avs_hdac_clock_gating_enable(bus, false);
>> +	snd_hdac_bus_enter_link_reset(bus);
>> +	avs_hdac_clock_gating_enable(bus, true);
>> +
>> +	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge)
>> +{
>> +	struct hdac_bus *bus = &adev->base.core;
>> +	struct hdac_ext_link *hlink;
>> +	int ret;
>> +
>> +	snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true);
>> +	avs_hdac_bus_init_chip(bus, true);
>> +
>> +	snd_hdac_ext_bus_ppcap_enable(bus, true);
>> +	snd_hdac_ext_bus_ppcap_int_enable(bus, true);
>> +
>> +	ret = avs_dsp_boot_firmware(adev, purge);
>> +	if (ret < 0) {
>> +		dev_err(adev->dev, "firmware boot failed: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* turn off the links that were off before suspend */
>> +	list_for_each_entry(hlink, &bus->hlink_list, list) {
>> +		if (!hlink->ref_count)
>> +			snd_hdac_ext_bus_link_power_down(hlink);
>> +	}
>> +
>> +	/* check dma status and clean up CORB/RIRB buffers */
>> +	if (!bus->cmd_dma_state)
>> +		snd_hdac_bus_stop_cmd_io(bus);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused avs_suspend(struct device *dev)
>> +{
>> +	return avs_suspend_common(to_avs_dev(dev), true);
>> +}
>> +
>> +static int __maybe_unused avs_resume(struct device *dev)
>> +{
>> +	return avs_resume_common(to_avs_dev(dev), true, true);
>> +}
>> +
>> +static int __maybe_unused avs_runtime_suspend(struct device *dev)
>> +{
>> +	return avs_suspend_common(to_avs_dev(dev), true);
>> +}
>> +
>> +static int __maybe_unused avs_runtime_resume(struct device *dev)
>> +{
>> +	return avs_resume_common(to_avs_dev(dev), true, false);
>> +}
>> +
>> +static int __maybe_unused avs_freeze(struct device *dev)
>> +{
>> +	return avs_suspend_common(to_avs_dev(dev), false);
>> +}
>> +static int __maybe_unused avs_thaw(struct device *dev)
>> +{
>> +	return avs_resume_common(to_avs_dev(dev), false, true);
>> +}
>> +
>> +static int __maybe_unused avs_poweroff(struct device *dev)
>> +{
>> +	return avs_suspend_common(to_avs_dev(dev), false);
>> +}
>> +
>> +static int __maybe_unused avs_restore(struct device *dev)
>> +{
>> +	return avs_resume_common(to_avs_dev(dev), false, true);
>> +}
>> +
>> +static const struct dev_pm_ops avs_dev_pm = {
>> +	.suspend = avs_suspend,
>> +	.resume = avs_resume,
> 
> ... all the 4 functions below seem unnecessary?


If we exclude 'low_power' then this is true. Will move this to the 
'low_power' patch as requested!

>> +	.freeze = avs_freeze,
>> +	.thaw = avs_thaw,
>> +	.poweroff = avs_poweroff,
>> +	.restore = avs_restore,
> 
> we've historically never used those, what drives this new usage?
> 
> on the SOF side, we've used prepare and complete, along with idle...


No streams may survive S4 what is not the case for S3. That's the main 
difference.

Yes, there are many opses vailable in dev_pm_ops, perhaps a different 
set of them is more appropriate. Let's have this talk once 'low_power' 
patch is here (PCM-power management patches are not part of this 
patchset but depend on it).

>> +	SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL)
>> +};
>> +
>>   static const struct pci_device_id avs_ids[] = {
>>   	{ 0 }
>>   };
>> @@ -546,6 +668,9 @@ static struct pci_driver avs_pci_driver = {
>>   	.id_table = avs_ids,
>>   	.probe = avs_pci_probe,
>>   	.remove = avs_pci_remove,
>> +	.driver = {
>> +		.pm = &avs_dev_pm,
>> +	},
>>   };
>>   module_pci_driver(avs_pci_driver);
>>   

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-04-26 22:12   ` Pierre-Louis Bossart
@ 2022-04-29 14:01     ` Cezary Rojewski
  2022-05-04  9:41       ` Amadeusz Sławiński
  0 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-29 14:01 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-27 12:12 AM, Pierre-Louis Bossart wrote:
> On 4/26/22 12:23, Cezary Rojewski wrote:
>> AVS driver operates with granular audio card division in mind.
>> Super-card approach (e.g.: I2S, DMIC and HDA DAIs combined) is
>> deprecated in favour of individual cards - one per each device. This
>> provides necessary dynamism, especially for configurations with number
>> of codecs present and makes it easier to survive auxiliary devices
>> failures - one card failing to probe does not prevent others from
>> succeeding.
>>
>> All boards spawned by AVS are unregistered on ->remove(). This includes
>> dummy codecs such as DMIC.
>>
>> As all machine boards found in sound/soc/intel/boards are irreversibly
>> tied to 'super-card' approach, new boards are going to be introduced.
>> This temporarily increases number of boards available under /intel
>> directory until skylake-driver becomes deprecated and removed.
> 
> I thought you wanted your own directory for cards, what's the point of adding new machine drivers in intel/boards if they ONLY work with your AVS driver?
> 
> Also you can only remove the machine drivers that are NOT shared with SOF...


Yes, if something is being actively used even once skylake-driver is 
removed, it will stay there. No worries. I recommend moving SOF-specific 
boards into /sof/intel/boards/ though - it feels logical to have 
driver-specific boards within driver-specific folder as very limited 
number of boards, if any, are "common" here.

I've provided board-related patchset on the list simultaneously so 
people can see the full picture.

>> +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",
>> +		.link_mask = AVS_SSP(0),
> 
> I've told this before, 'link_mask' was introduced for *SoundWire*. Please do not overload existing concepts and use this instead:
> 
> @i2s_link_mask: I2S/TDM links enabled on the board


Noooo :( Sad panda is sad.

'link_mask' is such a wonderful name as it matches naming used in our 
specs - which call BE side 'LINK'.

If it's a must then yes, we will resign from using 'link_mask'.

>> +		.tplg_filename = "skl-rt286-tplg.bin",
>> +	},
>> +	{
>> +		.id = "10508825",
>> +		.drv_name = "avs_nau8825",
>> +		.link_mask = AVS_SSP(1),
>> +		.tplg_filename = "skl-nau8825-tplg.bin",
>> +	},
>> +	{
>> +		.id = "INT343B",
>> +		.drv_name = "avs_ssm4567",
>> +		.link_mask = AVS_SSP(0),
>> +		.tplg_filename = "skl-ssm4567-tplg.bin",
>> +	},
>> +	{
>> +		.id = "MX98357A",
>> +		.drv_name = "avs_max98357a",
>> +		.link_mask = AVS_SSP(0),
>> +		.tplg_filename = "skl-max98357a-tplg.bin",
>> +	},
>> +	{},
>> +};
>> +
>> +static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
>> +	{
>> +		.id = "INT343A",
>> +		.drv_name = "avs_rt286",
>> +		.link_mask = AVS_SSP(0),
>> +		.quirk_data = &kbl_dmi_table,
>> +		.machine_quirk = dmi_match_quirk,
>> +		.tplg_filename = "kbl-rt286-tplg.bin",
>> +	},
>> +	{
>> +		.id = "INT343A",
>> +		.drv_name = "avs_rt298",
>> +		.link_mask = AVS_SSP(0),
>> +		.quirk_data = &kbl_r_dmi_table,
>> +		.machine_quirk = dmi_match_quirk,
>> +		.tplg_filename = "kblr-rt298-tplg.bin",
>> +	},
>> +	{
>> +		.id = "MX98373",
>> +		.drv_name = "avs_max98373",
>> +		.link_mask = AVS_SSP(0),
>> +		.tplg_filename = "kbl-max98373-tplg.bin",
>> +	},
>> +	{
>> +		.id = "DLGS7219",
>> +		.drv_name = "avs_da7219",
>> +		.link_mask = AVS_SSP(1),
>> +		.tplg_filename = "kbl-da7219-tplg.bin",
>> +	},
>> +	{},
>> +};
>> +

...

>> +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 */
>> +	{},
> 
> you are not using the intel/commmon matching and ACPI tables so I would recommend you deal with machine drivers in your private space.


And that's what we chose to do! I'm sorry if the message brought any 
confusion here. sound/soc/intel/avs/boards is the subdirectory for 
avs-driver boards.

>> +static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
>> +{
>> +	struct snd_soc_acpi_mach mach = {{0}};
>> +	struct platform_device *board;
>> +	struct hdac_device *hdev = &codec->core;
>> +	char *pname;
>> +	int ret, id;
>> +
>> +	pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev));
>> +	if (!pname)
>> +		return -ENOMEM;
>> +
>> +	ret = avs_hda_platform_register(adev, pname);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	mach.pdata = codec;
>> +	mach.mach_params.platform = pname;
>> +	mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
>> +					    hdev->vendor_id);
> 
> this is surprising, how many topologies will you end-up supporting then? Topologies are typically NOT dependent on the HDaudio codec type or vendor and only deal with HDaudio link DMA configurations.


Keen eye. I'm not providing all the patches simultaneously so the 
patchsets are easier to review. Note that avs/core.c (available on 
upstream) still provides 'NULL' for its hda_ext_ops. Separate patches 
for the point you brought here (and the completing the avs 
initialization for that matter) will be sent later.

The default: be specific when choosing topology for specific board - 
allows for tailoring configuration-specific topology. However, if there 
is no such files, load "generic" topology instead.

In our repo, most of the hda-XXXX-tplg.bin files are actually symlinks 
to few, real HD-Audio codec topologies.

>> +	if (!mach.tplg_filename)
>> +		return -ENOMEM;
>> +
>> +	id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr;
>> +	board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach,
>> +					      sizeof(mach));
>> +	if (IS_ERR(board)) {
>> +		dev_err(adev->dev, "hda 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;
>> +}
>> +

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 08/14] ASoC: Intel: avs: D0ix power state support
  2022-04-26 21:58   ` Pierre-Louis Bossart
@ 2022-04-29 14:19     ` Cezary Rojewski
  2022-04-29 14:33       ` Cezary Rojewski
  0 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-29 14:19 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:58 PM, Pierre-Louis Bossart wrote:
> On 4/26/22 12:23, Cezary Rojewski wrote:
>> Audio DSP device supports D0 substates in form of D0ix, allowing for
>> preserving more power even when device is still considered active (D0).
>> When entered, certain domains which are not being currently used become
>> power gated. Entering and leaving D0ix is a complex process and differs
>> between firmware generations.
>>
>> Conditions that disallow D0i3 and require immediate D0i0 transition
>> include but may not be limited to: IPC traffic, firmware tracing and
>> SRAM I/O. To make D0ix toggling sane, delay D0i3 transition and refresh
>> the timer each time an IPC is requrested.
> 
> typo: requested.

Ack.

> I find it odd to list all kinds of criteria but only handle one in the end. Do the other matter, is this a TODO, unclear what you are trying to say.


Good question. Firmware tracing code is part of debugfs.c file which has 
not yet been shared. But all other usages, not listed here, come down to 
invoking enable_d0ix() or disable_d0ix() whenever given operation blocks 
DSP from transitioning to D0iX.

Other usages such as directly accessing SRAM (outside of IPC handling) 
is non-existant in the avs-driver. When IPCs, most firmware generations 
take care of toggling d0ix for you.

>>   int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
>> diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
>> index 3ff17bd22a5a..2f18b137ff42 100644
>> --- a/sound/soc/intel/avs/dsp.c
>> +++ b/sound/soc/intel/avs/dsp.c
>> @@ -152,6 +152,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
>>   
>>   	adev->core_refs[core_id]++;
>>   	if (adev->core_refs[core_id] == 1) {
>> +		/*
>> +		 * No cores other than main-core can be running for DSP
>> +		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
> 
> conscious failure? what's that?


Any IPC failure which does not end in firmware throwing an exception or 
failing to deliver the response (IPC timeout). Sending response with 
status=<some error> is still a valid response.

>> +		 * simply d0ix power state will no longer be attempted.
>> +		 */
>> +		ret = avs_dsp_disable_d0ix(adev);
>> +		if (ret && ret != -AVS_EIPC)
>> +			goto err_disable_d0ix;
>> +
>>   		ret = avs_dsp_enable(adev, mask);
>>   		if (ret)
>>   			goto err_enable_dsp;
> tatic int
>> +avs_dsp_set_d0ix(struct avs_dev *adev, bool enable)
>> +{
>> +	struct avs_ipc *ipc = adev->ipc;
>> +	int ret;
>> +
>> +	/* Is transition required? */
>> +	if (ipc->in_d0ix == enable)
>> +		return 0;
>> +
>> +	ret = avs_dsp_op(adev, set_d0ix, enable);
>> +	if (ret) {
>> +		/* Prevent further d0ix attempts on conscious IPC failure. */
> 
> ??

Same as above but as I'm not sure whether '??' relates to comment above 
or the usage of 'conscious' word, I'll add to that:

To improve user-experience, we block any d0ix further d0ix attempts if 
even one SET_D0IX IPC fails. Audio can be streamed just fine without 
d0ix substate albeit it might not be as power efficient as with 
transition enabled.

>> +		if (ret == -AVS_EIPC)
>> +			atomic_inc(&ipc->d0ix_disable_depth);
>> +
>> +		ipc->in_d0ix = false;
>> +		return ret;
>> +	}
>> +
>> +	ipc->in_d0ix = enable;
>> +	return 0;
>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 08/14] ASoC: Intel: avs: D0ix power state support
  2022-04-29 14:19     ` Cezary Rojewski
@ 2022-04-29 14:33       ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-04-29 14:33 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-29 4:19 PM, Cezary Rojewski wrote:
> On 2022-04-26 11:58 PM, Pierre-Louis Bossart wrote:
>> On 4/26/22 12:23, Cezary Rojewski wrote:
>>> Audio DSP device supports D0 substates in form of D0ix, allowing for
>>> preserving more power even when device is still considered active (D0).
>>> When entered, certain domains which are not being currently used become
>>> power gated. Entering and leaving D0ix is a complex process and differs
>>> between firmware generations.
>>>
>>> Conditions that disallow D0i3 and require immediate D0i0 transition
>>> include but may not be limited to: IPC traffic, firmware tracing and
>>> SRAM I/O. To make D0ix toggling sane, delay D0i3 transition and refresh
>>> the timer each time an IPC is requrested.
>>
>> typo: requested.
> 
> Ack.
> 
>> I find it odd to list all kinds of criteria but only handle one in the 
>> end. Do the other matter, is this a TODO, unclear what you are trying 
>> to say.
> 
> 
> Good question. Firmware tracing code is part of debugfs.c file which has 
> not yet been shared. But all other usages, not listed here, come down to 
> invoking enable_d0ix() or disable_d0ix() whenever given operation blocks 
> DSP from transitioning to D0iX.
> 
> Other usages such as directly accessing SRAM (outside of IPC handling) 
> is non-existant in the avs-driver. When IPCs, most firmware generations 
> take care of toggling d0ix for you.


Sorry for the million typos.

In the last one what I meant is: directly accessing SRAM is a separate 
case, that is, when done outside of IPC protocol. We do not do that in 
the avs-driver. Why IPC protocol is 'so special'? Most firmware 
generations take care of toggling D0iX for the software so there is no 
need to disable the transition, read the reply from SRAM, do anything 
else necessary and re-enable it. Note: it's not true for all the 
generations :)

Regardless of the firmware generation used, software should be smart 
about choosing the right time for the transition. If we were to 
transition blindly after every single IPC, DSP would probably end up 
consuming more power than if no D0iX request were ever sent.

Regards,
Czarek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw
  2022-04-26 21:21   ` Pierre-Louis Bossart
@ 2022-05-01  9:45     ` Cezary Rojewski
  2022-05-06 15:25       ` Piotr Maziarz
  0 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-01  9:45 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:21 PM, Pierre-Louis Bossart wrote:
> On 4/26/22 12:23, Cezary Rojewski wrote:

...

>> diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
>> index c47f85161d95..de98f4c3adf8 100644
>> --- a/sound/soc/intel/avs/loader.c
>> +++ b/sound/soc/intel/avs/loader.c
>> @@ -15,6 +15,7 @@
>>   #include "cldma.h"
>>   #include "messages.h"
>>   #include "registers.h"
>> +#include "topology.h"
>>   
>>   #define AVS_ROM_STS_MASK		0xFF
>>   #define AVS_ROM_INIT_DONE		0x1
>> @@ -466,6 +467,70 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
>>   	return 0;
>>   }
>>   
>> +int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs)
>> +{
>> +	int start, id, i = 0;
>> +	int ret;
>> +
>> +	/* Calculate the id to assign for the next lib. */
>> +	for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
>> +		if (adev->lib_names[id][0] == '\0')
>> +			break;
>> +	if (id + num_libs >= adev->fw_cfg.max_libs_count)
>> +		return -EINVAL;
> 
> use ida_alloc_max() ?


After reading this one couple of times I'm keen to agree that IDA should 
have been used for library ID allocation and a at the same time, 
surprised it has't done that already. Till now we used IDA 'only' when 
allocating pipeline IDs and module instance IDs. Pipeline allocation is 
good comparison here - makes use of ida_alloc_max() already - library 
one should follow.

This finding is much appreciated, Pierre.

>> +
>> +	start = id;
>> +	while (i < num_libs) {
>> +		struct avs_fw_manifest *man;
>> +		const struct firmware *fw;
>> +		struct firmware stripped_fw;
>> +		char *filename;
>> +		int j;
>> +
>> +		filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name,
>> +				     libs[i].name);
>> +		if (!filename)
>> +			return -ENOMEM;
>> +
>> +		ret = avs_request_firmware(adev, &fw, filename);
>> +		kfree(filename);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		stripped_fw = *fw;
>> +		ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
>> +		if (ret) {
>> +			dev_err(adev->dev, "invalid library data: %d\n", ret);
>> +			goto release_fw;
>> +		}
>> +
>> +		ret = avs_fw_manifest_offset(&stripped_fw);
>> +		if (ret < 0)
>> +			goto release_fw;
>> +		man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
>> +
>> +		/* Don't load anything that's already in DSP memory. */
>> +		for (j = 0; j < id; j++)
>> +			if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE))
>> +				goto next_lib;
>> +
>> +		ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
>> +		if (ret)
>> +			goto release_fw;
>> +
>> +		strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
>> +		id++;
>> +next_lib:
>> +		i++;
>> +	}
>> +
>> +	return start == id ? 1 : 0;
>> +
>> +release_fw:
>> +	avs_release_last_firmware(adev);
> 
> why release only the last library and not all the ones that were previous loaded?
> or why bother to even release the last since at this point you probably need to restart completely?


Yes, avs_release_last_firmware() is used to clean 'locally' and indeed, 
failing to load a library will most likely end-up is complete restart.

I'll provide an internal build with this part removed and have 
validation do some testing first as performing 
avs_release_last_firmware() keeps things sane i.e. no invalid entries in 
the ->fw_list, no unnecessarily occupied memory for the already invalid 
entry and such.

In regard to *why is only last one released*, it's tied to how base 
firmware behaves. Assuming a scenario where two libraries need to be 
loaded, if the loading procedure for the second fails, base firmware 
will NOT unload/unmap allocated memory and resources for the first one. 
At the same time, there is no capability to unload library on demand. In 
order to rollback the transaction, DSP has to be shut down entirely, 
just like if we were talking about firmware exception or IPC timeout.

So, by doing what we do here, driver reflects firmware side 
(MODULES_INFO) basically 1:1.

Another good one, Pierre.

>> +	return ret;
>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver
  2022-04-26 21:33   ` Pierre-Louis Bossart
@ 2022-05-01 10:45     ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-01 10:45 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:33 PM, Pierre-Louis Bossart wrote:
>> +struct avs_dma_data {
>> +	struct avs_tplg_path_template *template;
>> +	struct avs_path *path;
>> +	/*
>> +	 * link stream is stored within substream's runtime
>> +	 * private_data to fulfill the needs of codec BE path
>> +	 *
>> +	 * host stream assigned
> 
> not able to parse that comment, what are you trying to say?


Sure. This actually stems from the legacy driver architecture. PCM 
operations for legacy HD-Audio are found in 
sound/pci/hda/hda_controller.c with respective types declared in 
sound/pci/hda/hda_controller.h. Operations found there make use of 
struct azx_dev and get_azx_dev(). So, to be able to re-use sound/pci/hda 
as much as possible plus declare virtually no custom HD-Audio related 
logic, we satisfy the needs of lower level code by assigning BE stream 
to substream->runtime->private_data.

As HD-Audio legacy operates in coupled mode only, there is no need for 
it to differentiate between HOST and LINK side. It's not true for DSP 
configurations though. So, we declare separate pointer for HOST stream 
allowing PCM operations found here to have easy access to both, LINK and 
HOST streams whenever necessary.

>> +	 */
>> +	struct hdac_ext_stream *host_stream;
>> +};
>> +
>> +static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count,
>> +				  loff_t *ppos)
>> +{
>> +	struct snd_soc_component *component = file->private_data;
>> +	struct snd_soc_card *card = component->card;
>> +	struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
>> +	char buf[64];
>> +	size_t len;
>> +
>> +	len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix,
>> +		       mach->tplg_filename);
>> +
>> +	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
>> +}
>> +
>> +static const struct file_operations topology_name_fops = {
>> +	.open = simple_open,
>> +	.read = topology_name_read,
>> +	.llseek = default_llseek,
>> +};
> 
> can you clarify why this is needed?


The usage of default_llseek() or the topology_name_fops as a whole? The 
latter is here to obtain name of the firmware file requested by given 
machine board easily from the debugfs. The former is probably just a 
copy-paste from other declarations of file_operation entries for 
READ-only files.

>> +
>> +static int avs_component_load_libraries(struct avs_soc_component *acomp)
>> +{
>> +	struct avs_tplg *tplg = acomp->tplg;
>> +	struct avs_dev *adev = to_avs_dev(acomp->base.dev);
>> +	int ret;
>> +
>> +	if (!tplg->num_libs)
>> +		return 0;
>> +
>> +	/* Parent device may be asleep and library loading involves IPCs. */
>> +	ret = pm_runtime_get_sync(adev->dev);
>> +	if (ret < 0 && ret != -EACCES) {
>> +		pm_runtime_put_noidle(adev->dev);
>> +		return ret;
>> +	}
> 
> Mark recommends the use of pm_runtime_resume_and_get(), see patches from today.


Will definitely check this out, thanks for pointing this out.

>> +
>> +	avs_hda_clock_gating_enable(adev, false);
>> +	avs_hda_l1sen_enable(adev, false);
>> +
>> +	ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs);
>> +
>> +	avs_hda_l1sen_enable(adev, true);
>> +	avs_hda_clock_gating_enable(adev, true);
>> +
>> +	if (!ret)
>> +		ret = avs_module_info_init(adev, false);
>> +
>> +	pm_runtime_mark_last_busy(adev->dev);
>> +	pm_runtime_put_autosuspend(adev->dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static int avs_component_probe(struct snd_soc_component *component)
>> +{
>> +	struct snd_soc_card *card = component->card;
>> +	struct snd_soc_acpi_mach *mach;
>> +	struct avs_soc_component *acomp;
>> +	struct avs_dev *adev;
>> +	char *filename;
>> +	int ret;
>> +
>> +	dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
>> +	mach = dev_get_platdata(card->dev);
>> +	acomp = to_avs_soc_component(component);
>> +	adev = to_avs_dev(component->dev);
>> +
>> +	acomp->tplg = avs_tplg_new(component);
>> +	if (!acomp->tplg)
>> +		return -ENOMEM;
>> +
>> +	if (!mach->tplg_filename)
>> +		goto finalize;
>> +
>> +	/* Load specified topology and create sysfs for it. */
>> +	filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
>> +			     mach->tplg_filename);
> 
> what is the link between topology and sysfs?


This comment is misleading as debugfs entry is being created as seen in 
the code below.

To answer the question assuming s/sysfs/debugfs/:

Ability to allow for reading name of the file requested by given machine 
board from user space. AVS driver spawns multiple machine boards, each 
requesting a topology file. To make it easier for the validation and 
normal users to understand what has requested what, we provide a simple, 
read-only debugfs entry for each of the boards within the avs tree.

>> +	if (!filename)
>> +		return -ENOMEM;
>> +
>> +	ret = avs_load_topology(component, filename);
>> +	kfree(filename);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = avs_component_load_libraries(acomp);
>> +	if (ret < 0) {
>> +		dev_err(card->dev, "libraries loading failed: %d\n", ret);
>> +		goto err_load_libs;
>> +	}
>> +
>> +finalize:
>> +	debugfs_create_file("topology_name", 0444, component->debugfs_root, component,
>> +			    &topology_name_fops);
> 
> that's debugfs here, is this to make it possible to select an alternate topology file? If yes, a comment earlier wouldn't hurt.


Nah, its purpose is very basic: get the exact name of the topology file 
loaded. Example:

intel/avs/hda-8086280b-tplg.bin

>> +
>> +	mutex_lock(&adev->comp_list_mutex);
>> +	list_add_tail(&acomp->node, &adev->comp_list);
>> +	mutex_unlock(&adev->comp_list_mutex);
>> +
>> +	return 0;
>> +
>> +err_load_libs:
>> +	avs_remove_topology(component);
>> +	return ret;
>> +}
>> +
> 
> 
>> +static const struct snd_soc_component_driver avs_component_driver = {
>> +	.name			= "avs-pcm",
>> +	.probe			= avs_component_probe,
>> +	.remove			= avs_component_remove,
>> +	.open			= avs_component_open,
>> +	.pointer		= avs_component_pointer,
>> +	.mmap			= avs_component_mmap,
>> +	.pcm_construct		= avs_component_construct,
>> +	.module_get_upon_open	= 1, /* increment refcount when a pcm is opened */
>> +	.topology_name_prefix	= "intel/avs",
> 
> is this intentional that the firmware binaries and topologies will be stored in the same intel/avs directory?
> 
>> +	.non_legacy_dai_naming	= true,
> 
> is this needed? we've never used this for Intel?


I'll recheck this but back in time when we drawn PCM ops for the first 
time there was a reason, certainly. Out of my head - impacts behavior of 
snd_soc_register_dais().

>> +};
>> +

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations
  2022-04-26 21:40   ` Pierre-Louis Bossart
@ 2022-05-01 10:48     ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-01 10:48 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:40 PM, Pierre-Louis Bossart wrote:
> 
>> +static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
>> +				     struct snd_soc_dai *dai)
>> +{
>> +	struct avs_dma_data *data;
>> +	int ret = 0;
>> +
>> +	data = snd_soc_dai_get_dma_data(dai, substream);
>> +
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
>> +		if (ret < 0)
>> +			dev_err(dai->dev, "run BE path failed: %d\n", ret);
>> +		break;
>> +
>> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +	case SNDRV_PCM_TRIGGER_STOP:
>> +		ret = avs_path_pause(data->path);
>> +		if (ret < 0)
>> +			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
>> +
>> +		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
>> +			ret = avs_path_reset(data->path);
>> +			if (ret < 0)
>> +				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
>> +		}
>> +		break;
>> +
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
> 
> TRIGGER_SUSPEND will result in -EINVAL?
> 
> Have you tried suspend-resume while playing audio?


The suspend/resume support for the PCM streaming is split and part of a 
separate patch. Because of this, all related triggers have been skipped 
in current series.

>> +	}
>> +
>> +	return ret;
>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 05/14] ASoC: Intel: avs: HDA PCM BE operations
  2022-04-26 21:45   ` Pierre-Louis Bossart
@ 2022-05-01 10:55     ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-01 10:55 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:45 PM, Pierre-Louis Bossart wrote:
>> +static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
>> +{
>> +	struct avs_dma_data *data;
>> +	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
>> +	struct hdac_ext_stream *estream;
> 
> host_stream, estream, there seems to be multiple naming conventions for the same thing?

I see your point. Have the stream be called either host_ or link_ 
depending on which end we speak of (FE/BE). Current pattern:

- hstream for hdac_stream instances
- estream for hdac_ext_stream instances

Will reiterate and come back :)

>> +	struct hdac_ext_link *link;
>> +	struct hda_codec *codec;
>> +
>> +	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
>> +
>> +	data = snd_soc_dai_get_dma_data(dai, substream);
>> +	if (!data->path)
>> +		return 0;
>> +
>> +	estream = substream->runtime->private_data;
>> +	estream->link_prepared = false;
>> +	avs_path_free(data->path);
>> +	data->path = NULL;
>> +
>> +	/* clear link <-> stream mapping */
>> +	codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev);
>> +	link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr);
>> +	if (!link)
>> +		return -EINVAL;
>> +
>> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> +		snd_hdac_ext_link_clear_stream_id(link, estream->hstream.stream_tag);
>> +
>> +	return 0;
>> +}
> 
>> +static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
>> +				  struct snd_soc_dai *dai)
>> +{
>> +	struct hdac_ext_stream *estream;
>> +	struct avs_dma_data *data;
>> +	int ret = 0;
>> +
>> +	dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd);
>> +
>> +	data = snd_soc_dai_get_dma_data(dai, substream);
>> +	estream = substream->runtime->private_data;
>> +
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +		snd_hdac_ext_link_stream_start(estream);
>> +
>> +		ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
>> +		if (ret < 0)
>> +			dev_err(dai->dev, "run BE path failed: %d\n", ret);
>> +		break;
>> +
>> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +	case SNDRV_PCM_TRIGGER_STOP:
>> +		ret = avs_path_pause(data->path);
>> +		if (ret < 0)
>> +			dev_err(dai->dev, "pause BE path failed: %d\n", ret);
>> +
>> +		snd_hdac_ext_link_stream_clear(estream);
>> +
>> +		if (cmd == SNDRV_PCM_TRIGGER_STOP) {
>> +			ret = avs_path_reset(data->path);
>> +			if (ret < 0)
>> +				dev_err(dai->dev, "reset BE path failed: %d\n", ret);
>> +		}
>> +		break;
>> +
>> +	default:
>> +		ret = -EINVAL;
> 
> TRIGGER_SUSPEND?


As explained in previous patch, PM for PCM streaming is part of a 
separate patch (not found on the list yet). Because of this, all related 
triggers are ignored in current series.

>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
> 
>> +static const struct snd_soc_component_driver avs_hda_component_driver = {
>> +	.name			= "avs-hda-pcm",
>> +	.probe			= avs_component_hda_probe,
>> +	.remove			= avs_component_hda_remove,
>> +	.open			= avs_component_hda_open,
>> +	.close			= avs_component_hda_close,
>> +	.pointer		= avs_component_pointer,
>> +	.mmap			= avs_component_mmap,
>> +	.pcm_construct		= avs_component_construct,
>> +	/*
>> +	 * hda platform component's probe() is dependent on
>> +	 * codec->pcm_list_head, it needs to be initialized after codec
>> +	 * component. remove_order is here for completeness sake
>> +	 */
>> +	.probe_order		= SND_SOC_COMP_ORDER_LATE,
>> +	.remove_order		= SND_SOC_COMP_ORDER_EARLY,
>> +	.module_get_upon_open	= 1,
>> +	.topology_name_prefix	= "intel/avs",
>> +	.non_legacy_dai_naming	= true,
> 
> needed?


Similarly as in previous patch - will re-check. Probably impacts (or 
impacted in the past) DAI registering process.

>> +};
>> +
>> +int avs_hda_platform_register(struct avs_dev *adev, const char *name)
>> +{
>> +	return avs_soc_component_register(adev->dev, name,
>> +					  &avs_hda_component_driver, NULL, 0);
>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow
  2022-04-26 21:53   ` Pierre-Louis Bossart
@ 2022-05-01 15:32     ` Cezary Rojewski
  2022-05-02 13:53       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-01 15:32 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-04-26 11:53 PM, Pierre-Louis Bossart wrote:
> On 4/26/22 12:23, Cezary Rojewski wrote:
>> In rare occassions, under stress conditions or hardware malfunction, DSP
> 
> occasions


Ack.

>> firmware may fail. Software is notified about such situation with
>> EXCEPTION_CAUGHT notification. IPC timeout is also counted as critical
>> device failure. More often than not, driver can recover from such
>> situations by performing full reset: killing and restarting ADSP.
>>
>> Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
>> Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
>> ---
>>   sound/soc/intel/Kconfig        |  1 +
>>   sound/soc/intel/avs/avs.h      |  4 ++
>>   sound/soc/intel/avs/ipc.c      | 95 +++++++++++++++++++++++++++++++++-
>>   sound/soc/intel/avs/messages.h |  5 ++
>>   4 files changed, 103 insertions(+), 2 deletions(-)
>>
>> diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
>> index c364ddf22267..05ad6bdecfc5 100644
>> --- a/sound/soc/intel/Kconfig
>> +++ b/sound/soc/intel/Kconfig
>> @@ -218,6 +218,7 @@ config SND_SOC_INTEL_AVS
>>   	select SND_HDA_EXT_CORE
>>   	select SND_HDA_DSP_LOADER
>>   	select SND_INTEL_NHLT
>> +	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/avs.h b/sound/soc/intel/avs/avs.h
>> index e628f78d1864..02c2aa1bcd5c 100644
>> --- a/sound/soc/intel/avs/avs.h
>> +++ b/sound/soc/intel/avs/avs.h
>> @@ -42,6 +42,7 @@ 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 coredump)(struct avs_dev *, union avs_notify_msg *);
>>   };
>>   
>>   #define avs_dsp_op(adev, op, ...) \
>> @@ -164,12 +165,15 @@ struct avs_ipc {
>>   	struct avs_ipc_msg rx;
>>   	u32 default_timeout_ms;
>>   	bool ready;
>> +	bool 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;
>>   };
>>   
>>   #define AVS_EIPC	EREMOTEIO
>> diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
>> index 68aaf01edbf2..84cb411c82fa 100644
>> --- a/sound/soc/intel/avs/ipc.c
>> +++ b/sound/soc/intel/avs/ipc.c
>> @@ -14,6 +14,87 @@
>>   
>>   #define AVS_IPC_TIMEOUT_MS	300
>>   
>> +static void avs_dsp_recovery(struct avs_dev *adev)
>> +{
>> +	struct avs_soc_component *acomp;
>> +	unsigned int core_mask;
>> +	int ret;
>> +
>> +	if (adev->ipc->recovering)
>> +		return;
>> +	adev->ipc->recovering = true;
> 
> don't you need some sort of lock to test/clear this flag?

Our stress tests do not confirm this. I'll not ignore this warning 
though, will recheck with my team next week.

>> +
>> +	mutex_lock(&adev->comp_list_mutex);
>> +	/* disconnect all running streams */
>> +	list_for_each_entry(acomp, &adev->comp_list, node) {
>> +		struct snd_soc_pcm_runtime *rtd;
>> +		struct snd_soc_card *card;
>> +
>> +		card = acomp->base.card;
>> +		if (!card)
>> +			continue;
>> +
>> +		for_each_card_rtds(card, rtd) {
>> +			struct snd_pcm *pcm;
>> +			int dir;
>> +
>> +			pcm = rtd->pcm;
>> +			if (!pcm || rtd->dai_link->no_pcm)
>> +				continue;
>> +
>> +			for_each_pcm_streams(dir) {
>> +				struct snd_pcm_substream *substream;
>> +
>> +				substream = pcm->streams[dir].substream;
>> +				if (!substream || !substream->runtime)
>> +					continue;
>> +
>> +				snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
>> +			}
>> +		}
>> +	}
>> +	mutex_unlock(&adev->comp_list_mutex);
>> +
>> +	/* forcibly shutdown all cores */
>> +	core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
>> +	avs_dsp_core_disable(adev, core_mask);
>> +
>> +	/* attempt dsp reboot */
>> +	ret = avs_dsp_boot_firmware(adev, true);
>> +	if (ret < 0)
>> +		dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
>> +
>> +	pm_runtime_mark_last_busy(adev->dev);
>> +	pm_runtime_enable(adev->dev);
>> +	pm_request_autosuspend(adev->dev);
> 
> there are zero users of this routine in the entire sound/ tree, can you clarify why this is needed or what you are trying to do?


Unsure which routine you question here. I'll assume it's 
pm_request_autosuspend().

pm_request_audiosuspend() is being used to queue suspend once recovery 
completes. Recovery takes time and during that time all communication 
attempts with DSP will yield -EPERM. PM is also blocked for the device 
with pm_runtime_disable(), performed before scheduling the recovery 
work. Once recovery completes we do not just unblock the PM as that 
would cause immediate suspend. Instead, we "refresh" the *last busy* 
status and queue the suspend operation.

>> +
>> +	adev->ipc->recovering = false;
>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow
  2022-05-01 15:32     ` Cezary Rojewski
@ 2022-05-02 13:53       ` Pierre-Louis Bossart
  0 siblings, 0 replies; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-05-02 13:53 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma


>>> +
>>> +    /* forcibly shutdown all cores */
>>> +    core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0);
>>> +    avs_dsp_core_disable(adev, core_mask);
>>> +
>>> +    /* attempt dsp reboot */
>>> +    ret = avs_dsp_boot_firmware(adev, true);
>>> +    if (ret < 0)
>>> +        dev_err(adev->dev, "dsp reboot failed: %d\n", ret);
>>> +
>>> +    pm_runtime_mark_last_busy(adev->dev);
>>> +    pm_runtime_enable(adev->dev);
>>> +    pm_request_autosuspend(adev->dev);
>>
>> there are zero users of this routine in the entire sound/ tree, can you clarify why this is needed or what you are trying to do?
> 
> 
> Unsure which routine you question here. I'll assume it's pm_request_autosuspend().
> 
> pm_request_audiosuspend() is being used to queue suspend once recovery completes. Recovery takes time and during that time all communication attempts with DSP will yield -EPERM. PM is also blocked for the device with pm_runtime_disable(), performed before scheduling the recovery work. Once recovery completes we do not just unblock the PM as that would cause immediate suspend. Instead, we "refresh" the *last busy* status and queue the suspend operation.

But since you already have autosuspend enabled, why would you need to explicitly queue a suspend operation? What happens if that last call is omitted, is there actually a functional difference?

Not objecting if that's required, but since no one else used it so far I wonder if we missed something or if this is overkill.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-04-29 14:01     ` Cezary Rojewski
@ 2022-05-04  9:41       ` Amadeusz Sławiński
  2022-05-04 11:12         ` Cezary Rojewski
  2022-05-04 11:26         ` Péter Ujfalusi
  0 siblings, 2 replies; 40+ messages in thread
From: Amadeusz Sławiński @ 2022-05-04  9:41 UTC (permalink / raw)
  To: Cezary Rojewski, Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, cujomalainey, lma

On 4/29/2022 4:01 PM, Cezary Rojewski wrote:

>>> +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",
>>> +        .link_mask = AVS_SSP(0),
>>
>> I've told this before, 'link_mask' was introduced for *SoundWire*. 
>> Please do not overload existing concepts and use this instead:
>>
>> @i2s_link_mask: I2S/TDM links enabled on the board
> 
> 
> Noooo :( Sad panda is sad.
> 
> 'link_mask' is such a wonderful name as it matches naming used in our 
> specs - which call BE side 'LINK'.
> 
> If it's a must then yes, we will resign from using 'link_mask'.
> 

I'll just note that header kernel doc for link_mask says:
" * @link_mask: describes required board layout, e.g. for SoundWire."
I would say there is no assumption that it is SDW only, so we could also 
argue that if it is it should be named sdw_link_mask and comment be 
fixed to remove "e.g." ;)

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-05-04  9:41       ` Amadeusz Sławiński
@ 2022-05-04 11:12         ` Cezary Rojewski
  2022-05-04 11:26         ` Péter Ujfalusi
  1 sibling, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-04 11:12 UTC (permalink / raw)
  To: Amadeusz Sławiński, Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, cujomalainey, lma

On 2022-05-04 11:41 AM, Amadeusz Sławiński wrote:
> On 4/29/2022 4:01 PM, Cezary Rojewski wrote:
> 
>>>> +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",
>>>> +        .link_mask = AVS_SSP(0),
>>>
>>> I've told this before, 'link_mask' was introduced for *SoundWire*. 
>>> Please do not overload existing concepts and use this instead:
>>>
>>> @i2s_link_mask: I2S/TDM links enabled on the board
>>
>>
>> Noooo :( Sad panda is sad.
>>
>> 'link_mask' is such a wonderful name as it matches naming used in our 
>> specs - which call BE side 'LINK'.
>>
>> If it's a must then yes, we will resign from using 'link_mask'.
>>
> 
> I'll just note that header kernel doc for link_mask says:
> " * @link_mask: describes required board layout, e.g. for SoundWire."
> I would say there is no assumption that it is SDW only, so we could also 
> argue that if it is it should be named sdw_link_mask and comment be 
> fixed to remove "e.g." ;)


This is a marvelous suggestion! Adheres to fw spec, adheres to kernel 
doc. What more would one want!


Regards,
Czarek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-05-04  9:41       ` Amadeusz Sławiński
  2022-05-04 11:12         ` Cezary Rojewski
@ 2022-05-04 11:26         ` Péter Ujfalusi
  2022-05-04 12:33           ` Cezary Rojewski
  1 sibling, 1 reply; 40+ messages in thread
From: Péter Ujfalusi @ 2022-05-04 11:26 UTC (permalink / raw)
  To: alsa-devel



On 04/05/2022 12:41, Amadeusz Sławiński wrote:
> On 4/29/2022 4:01 PM, Cezary Rojewski wrote:
>>>> +#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",
>>>> +        .link_mask = AVS_SSP(0),
>>>
>>> I've told this before, 'link_mask' was introduced for *SoundWire*.
>>> Please do not overload existing concepts and use this instead:
>>>
>>> @i2s_link_mask: I2S/TDM links enabled on the board
>>
>>
>> Noooo :( Sad panda is sad.
>>
>> 'link_mask' is such a wonderful name as it matches naming used in our
>> specs - which call BE side 'LINK'.
>>
>> If it's a must then yes, we will resign from using 'link_mask'.
>>
> 
> I'll just note that header kernel doc for link_mask says:
> " * @link_mask: describes required board layout, e.g. for SoundWire."
> I would say there is no assumption that it is SDW only, so we could also
> argue that if it is it should be named sdw_link_mask and comment be
> fixed to remove "e.g." ;)

In mainline kernel it is (as of 5.18.0-rc5):
/**
 * snd_soc_acpi_mach_params: interface for machine driver configuration
 *
 * @acpi_ipc_irq_index: used for BYT-CR detection
 * @platform: string used for HDAudio codec support
 * @codec_mask: used for HDAudio support
 * @dmic_num: number of SoC- or chipset-attached PDM digital microphones
 * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
 * @link_mask: SoundWire links enabled on the board
 * @links: array of SoundWire link _ADR descriptors, null terminated
 * @i2s_link_mask: I2S/TDM links enabled on the board
 * @num_dai_drivers: number of elements in @dai_drivers
 * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode
 */

link_mask is for SDW
i2s_link_mask is for I2S

It might be nicer to prefix the bare link_mask with sdw, it might
happen, but the comment is pretty explicit. I would not dare to use
link_mask for DMIC for example...

-- 
Péter

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 10/14] ASoC: Intel: avs: Machine board registration
  2022-05-04 11:26         ` Péter Ujfalusi
@ 2022-05-04 12:33           ` Cezary Rojewski
  0 siblings, 0 replies; 40+ messages in thread
From: Cezary Rojewski @ 2022-05-04 12:33 UTC (permalink / raw)
  To: Péter Ujfalusi, alsa-devel

On 2022-05-04 1:26 PM, Péter Ujfalusi wrote:
> On 04/05/2022 12:41, Amadeusz Sławiński wrote:
>> On 4/29/2022 4:01 PM, Cezary Rojewski wrote:
>>>>> +#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",
>>>>> +        .link_mask = AVS_SSP(0),
>>>>
>>>> I've told this before, 'link_mask' was introduced for *SoundWire*.
>>>> Please do not overload existing concepts and use this instead:
>>>>
>>>> @i2s_link_mask: I2S/TDM links enabled on the board
>>>
>>>
>>> Noooo :( Sad panda is sad.
>>>
>>> 'link_mask' is such a wonderful name as it matches naming used in our
>>> specs - which call BE side 'LINK'.
>>>
>>> If it's a must then yes, we will resign from using 'link_mask'.
>>>
>>
>> I'll just note that header kernel doc for link_mask says:
>> " * @link_mask: describes required board layout, e.g. for SoundWire."
>> I would say there is no assumption that it is SDW only, so we could also
>> argue that if it is it should be named sdw_link_mask and comment be
>> fixed to remove "e.g." ;)
> 
> In mainline kernel it is (as of 5.18.0-rc5):
> /**
>   * snd_soc_acpi_mach_params: interface for machine driver configuration
>   *
>   * @acpi_ipc_irq_index: used for BYT-CR detection
>   * @platform: string used for HDAudio codec support
>   * @codec_mask: used for HDAudio support
>   * @dmic_num: number of SoC- or chipset-attached PDM digital microphones
>   * @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
>   * @link_mask: SoundWire links enabled on the board
>   * @links: array of SoundWire link _ADR descriptors, null terminated
>   * @i2s_link_mask: I2S/TDM links enabled on the board
>   * @num_dai_drivers: number of elements in @dai_drivers
>   * @dai_drivers: pointer to dai_drivers, used e.g. in nocodec mode
>   */
> 
> link_mask is for SDW
> i2s_link_mask is for I2S
> 
> It might be nicer to prefix the bare link_mask with sdw, it might
> happen, but the comment is pretty explicit. I would not dare to use
> link_mask for DMIC for example...
> 


Ok, only now I realized that the 'i2s_link_mask' is already part of 
upstream kernel - commit:

ASoC: soc-acpi: add information on I2S/TDM link mask

from Mar 8th 2022. I agree on the "sdw_" prefix part. Right now, the 
name gives plenty of room for interpretation.


Regards,
Czarek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw
  2022-05-01  9:45     ` Cezary Rojewski
@ 2022-05-06 15:25       ` Piotr Maziarz
  2022-05-06 15:47         ` Pierre-Louis Bossart
  0 siblings, 1 reply; 40+ messages in thread
From: Piotr Maziarz @ 2022-05-06 15:25 UTC (permalink / raw)
  To: Cezary Rojewski, Pierre-Louis Bossart, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma


On 2022-05-01 11:45, Cezary Rojewski wrote:
> On 2022-04-26 11:21 PM, Pierre-Louis Bossart wrote:
>> On 4/26/22 12:23, Cezary Rojewski wrote:
>
> ...
>
>>> diff --git a/sound/soc/intel/avs/loader.c 
>>> b/sound/soc/intel/avs/loader.c
>>> index c47f85161d95..de98f4c3adf8 100644
>>> --- a/sound/soc/intel/avs/loader.c
>>> +++ b/sound/soc/intel/avs/loader.c
>>> @@ -15,6 +15,7 @@
>>>   #include "cldma.h"
>>>   #include "messages.h"
>>>   #include "registers.h"
>>> +#include "topology.h"
>>>     #define AVS_ROM_STS_MASK        0xFF
>>>   #define AVS_ROM_INIT_DONE        0x1
>>> @@ -466,6 +467,70 @@ int avs_hda_transfer_modules(struct avs_dev 
>>> *adev, bool load,
>>>       return 0;
>>>   }
>>>   +int avs_dsp_load_libraries(struct avs_dev *adev, struct 
>>> avs_tplg_library *libs, u32 num_libs)
>>> +{
>>> +    int start, id, i = 0;
>>> +    int ret;
>>> +
>>> +    /* Calculate the id to assign for the next lib. */
>>> +    for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
>>> +        if (adev->lib_names[id][0] == '\0')
>>> +            break;
>>> +    if (id + num_libs >= adev->fw_cfg.max_libs_count)
>>> +        return -EINVAL;
>>
>> use ida_alloc_max() ?
>
>
> After reading this one couple of times I'm keen to agree that IDA 
> should have been used for library ID allocation and a at the same 
> time, surprised it has't done that already. Till now we used IDA 
> 'only' when allocating pipeline IDs and module instance IDs. Pipeline 
> allocation is good comparison here - makes use of ida_alloc_max() 
> already - library one should follow.
>
> This finding is much appreciated, Pierre.

I think that using ida here is a bit of an overkill. Ida works fine when 
there can be both id allocation and freeing and that's how it work with 
pipelines and modules IDs in avs. However there is no mechanism for 
unloading libraries in cAVS firmware, therefore ida would be used here 
only to increase the ID, so it needlessly complicates the code while not 
giving much of a benefit. Also our approach to check if we can load all 
libraries before the loop makes it problematic with ida because we would 
need to allocate an id at start and calculate if all libs would fit and 
then either free it instantly or complicate the loop to use id allocated 
before. Therefore I suggest to leave this code unchanged. I've synced 
with Cezary on this and provided explanation convinced him too.

>
>>> +
>>> +    start = id;
>>> +    while (i < num_libs) {
>>> +        struct avs_fw_manifest *man;
>>> +        const struct firmware *fw;
>>> +        struct firmware stripped_fw;
>>> +        char *filename;
>>> +        int j;
>>> +
>>> +        filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, 
>>> adev->spec->name,
>>> +                     libs[i].name);
>>> +        if (!filename)
>>> +            return -ENOMEM;
>>> +
>>> +        ret = avs_request_firmware(adev, &fw, filename);
>>> +        kfree(filename);
>>> +        if (ret < 0)
>>> +            return ret;
>>> +
>>> +        stripped_fw = *fw;
>>> +        ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL);
>>> +        if (ret) {
>>> +            dev_err(adev->dev, "invalid library data: %d\n", ret);
>>> +            goto release_fw;
>>> +        }
>>> +
>>> +        ret = avs_fw_manifest_offset(&stripped_fw);
>>> +        if (ret < 0)
>>> +            goto release_fw;
>>> +        man = (struct avs_fw_manifest *)(stripped_fw.data + ret);
>>> +
>>> +        /* Don't load anything that's already in DSP memory. */
>>> +        for (j = 0; j < id; j++)
>>> +            if (!strncmp(adev->lib_names[j], man->name, 
>>> AVS_LIB_NAME_SIZE))
>>> +                goto next_lib;
>>> +
>>> +        ret = avs_dsp_op(adev, load_lib, &stripped_fw, id);
>>> +        if (ret)
>>> +            goto release_fw;
>>> +
>>> +        strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE);
>>> +        id++;
>>> +next_lib:
>>> +        i++;
>>> +    }
>>> +
>>> +    return start == id ? 1 : 0;
>>> +
>>> +release_fw:
>>> +    avs_release_last_firmware(adev);
>>
>> why release only the last library and not all the ones that were 
>> previous loaded?
>> or why bother to even release the last since at this point you 
>> probably need to restart completely?
>
>
> Yes, avs_release_last_firmware() is used to clean 'locally' and 
> indeed, failing to load a library will most likely end-up is complete 
> restart.
>
> I'll provide an internal build with this part removed and have 
> validation do some testing first as performing 
> avs_release_last_firmware() keeps things sane i.e. no invalid entries 
> in the ->fw_list, no unnecessarily occupied memory for the already 
> invalid entry and such.
>
> In regard to *why is only last one released*, it's tied to how base 
> firmware behaves. Assuming a scenario where two libraries need to be 
> loaded, if the loading procedure for the second fails, base firmware 
> will NOT unload/unmap allocated memory and resources for the first 
> one. At the same time, there is no capability to unload library on 
> demand. In order to rollback the transaction, DSP has to be shut down 
> entirely, just like if we were talking about firmware exception or IPC 
> timeout.
>
> So, by doing what we do here, driver reflects firmware side 
> (MODULES_INFO) basically 1:1.
>
> Another good one, Pierre.
>
>>> +    return ret;
>>> +}

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw
  2022-05-06 15:25       ` Piotr Maziarz
@ 2022-05-06 15:47         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 40+ messages in thread
From: Pierre-Louis Bossart @ 2022-05-06 15:47 UTC (permalink / raw)
  To: Piotr Maziarz, Cezary Rojewski, alsa-devel, broonie
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede,
	amadeuszx.slawinski, cujomalainey, lma


>>>>   +int avs_dsp_load_libraries(struct avs_dev *adev, struct
>>>> avs_tplg_library *libs, u32 num_libs)
>>>> +{
>>>> +    int start, id, i = 0;
>>>> +    int ret;
>>>> +
>>>> +    /* Calculate the id to assign for the next lib. */
>>>> +    for (id = 0; id < adev->fw_cfg.max_libs_count; id++)
>>>> +        if (adev->lib_names[id][0] == '\0')
>>>> +            break;
>>>> +    if (id + num_libs >= adev->fw_cfg.max_libs_count)
>>>> +        return -EINVAL;
>>>
>>> use ida_alloc_max() ?
>>
>>
>> After reading this one couple of times I'm keen to agree that IDA
>> should have been used for library ID allocation and a at the same
>> time, surprised it has't done that already. Till now we used IDA
>> 'only' when allocating pipeline IDs and module instance IDs. Pipeline
>> allocation is good comparison here - makes use of ida_alloc_max()
>> already - library one should follow.
>>
>> This finding is much appreciated, Pierre.
> 
> I think that using ida here is a bit of an overkill. Ida works fine when
> there can be both id allocation and freeing and that's how it work with
> pipelines and modules IDs in avs. However there is no mechanism for
> unloading libraries in cAVS firmware, therefore ida would be used here
> only to increase the ID, so it needlessly complicates the code while not
> giving much of a benefit. Also our approach to check if we can load all
> libraries before the loop makes it problematic with ida because we would
> need to allocate an id at start and calculate if all libs would fit and
> then either free it instantly or complicate the loop to use id allocated
> before. Therefore I suggest to leave this code unchanged. I've synced
> with Cezary on this and provided explanation convinced him too.

That's fine, you should however capture this design decision with a
comment or a clarification in the commit message. "libraries" mean
different things to different people, and it's hard to review without
context.


^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2022-05-06 15:48 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-26 17:23 [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski
2022-04-26 17:23 ` [PATCH 01/14] ASoC: Intel: avs: Account for libraries when booting basefw Cezary Rojewski
2022-04-26 21:21   ` Pierre-Louis Bossart
2022-05-01  9:45     ` Cezary Rojewski
2022-05-06 15:25       ` Piotr Maziarz
2022-05-06 15:47         ` Pierre-Louis Bossart
2022-04-26 17:23 ` [PATCH 02/14] ASoC: Intel: avs: Generic soc component driver Cezary Rojewski
2022-04-26 21:33   ` Pierre-Louis Bossart
2022-05-01 10:45     ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 03/14] ASoC: Intel: avs: Generic PCM FE operations Cezary Rojewski
2022-04-26 17:23 ` [PATCH 04/14] ASoC: Intel: avs: non-HDA PCM BE operations Cezary Rojewski
2022-04-26 21:40   ` Pierre-Louis Bossart
2022-05-01 10:48     ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 05/14] ASoC: Intel: avs: HDA " Cezary Rojewski
2022-04-26 21:45   ` Pierre-Louis Bossart
2022-05-01 10:55     ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 06/14] ASoC: Intel: avs: Coredump and recovery flow Cezary Rojewski
2022-04-26 21:53   ` Pierre-Louis Bossart
2022-05-01 15:32     ` Cezary Rojewski
2022-05-02 13:53       ` Pierre-Louis Bossart
2022-04-26 17:23 ` [PATCH 07/14] ASoC: Intel: avs: Prepare for firmware tracing Cezary Rojewski
2022-04-26 17:23 ` [PATCH 08/14] ASoC: Intel: avs: D0ix power state support Cezary Rojewski
2022-04-26 21:58   ` Pierre-Louis Bossart
2022-04-29 14:19     ` Cezary Rojewski
2022-04-29 14:33       ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 09/14] ASoC: Intel: avs: Event tracing Cezary Rojewski
2022-04-26 17:23 ` [PATCH 10/14] ASoC: Intel: avs: Machine board registration Cezary Rojewski
2022-04-26 22:12   ` Pierre-Louis Bossart
2022-04-29 14:01     ` Cezary Rojewski
2022-05-04  9:41       ` Amadeusz Sławiński
2022-05-04 11:12         ` Cezary Rojewski
2022-05-04 11:26         ` Péter Ujfalusi
2022-05-04 12:33           ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 11/14] ASoC: Intel: avs: PCI driver implementation Cezary Rojewski
2022-04-26 17:23 ` [PATCH 12/14] ASoC: Intel: avs: Power management Cezary Rojewski
2022-04-26 22:18   ` Pierre-Louis Bossart
2022-04-29 13:44     ` Cezary Rojewski
2022-04-26 17:23 ` [PATCH 13/14] ASoC: Intel: avs: SKL-based platforms support Cezary Rojewski
2022-04-26 17:23 ` [PATCH 14/14] ASoC: Intel: avs: APL-based " Cezary Rojewski
2022-04-27  8:15 ` [PATCH 00/14] ASoC: Intel: avs: Driver core and PCM operations Cezary Rojewski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.