All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
@ 2022-02-07 12:20 Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 01/17] ALSA: hda: Add helper macros for DSP capable devices Cezary Rojewski
                   ` (18 more replies)
  0 siblings, 19 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

A continuation of cleanup work of Intel SST solutions found in
sound/soc/intel/. With two major chapters released last year catpt [1]
and removal of haswell solution [2], time has come for Skylake-driver.

Througout 2019, 2020 and 2021 Skylake-driver has had many fixes applied
and even attempts of refactors as seen in fundamental overhaul [3], IPC
flow adjustments [4] and LARGE_CONFIG overhaul [5] series.
Unfortunately, story repeats itself - problems are found within the core
of a driver. Painting it with different colors does not change the fact
that is it still a house of cards. As changes needed to address those
issues would make Skylake solution incompatible with its previous
revisions, a decision has been made to provide a new solution instead.
In time it would deprecate and replace Skylake-driver.

That solution has been called AVS - from AudioDSP architecture name:
Audio-Voice-Speech. It is meant to provide support for the exact same
range of platforms as its predecessor: SKL, KBL, AML and APL.

Note: this series is dependent upon HDA-series [6] which exposes several
codec-organization functions allowing for reduced code size on
avs-driver side.

Note: this series does not add fully functional driver as its size would
get out of control. Here, focus is put on adding IPC protocol and code
loading code.


Changes RFC v1 [7]: -> v1:
- separated HDA codec-organization patches, path and topology handling,
  PCM and complementary features such as recovery from this series to
  ease the review process
- fixed EXPORT_SYMBOL_GPL for exported members of ASoC framework
- result of stall() is now checked when sending ROM message
- result of snd_hdac_ext_stream_set_spib() is now checked when loading
  basefw
- if basefw is not ready, notification processing is now skipped
- documented several topology parsing helpers


Changes [internal] RFC v2 -> [public] RFC v1:
- dropped any sysfs related changes from this series, moved to follow up
  one
- dropped entire subscription-mechanism found in ipc.c. Handlers that
  are delegated to service certain firmware notifications are now called
  directly
- fixed kernel doc for snd_soc_dapm_new_dai_widgets() as reported by ikp
- prefixed snd_hda_codec_device_init() as suggested by Amadeo
- improved comments for d0ix transitions for APL-based platforms as
  suggested by Pierre
- a ton of spelling related fixes in most of the commit messages
- fixed remaining warnings pointed by scan-build (variable assigned but
  not used)
- replaced most of 'cAVS X.Y' expression usages with 'platform-based'
  equivalents as suggested by Pierre e.g.: cAVS 1.5 -> SKL-based


Changes [internal] RFC v1 -> [internal] RFC v2:

- fixed memleak caused by lack of kfree(vols) if memory allocation fails
  in avs_peakvol_create() as reported by Curtis
- fixed missing 'i' iterator incrementation in avs_widget_ready()
  causing reference loss as reported by Curtis
- replace hardcode: 0x40 usage with snd_hdac_calc_stream_format as
  suggested by Curtis.
  In consequence, readability for all code loading (CL) procedues has
  increased and such approach auto-documents the CL stream preparation

- updated behavior of all index-fetching functions found in utils.c:
  avs_module_entry_index(), avs_module_id_entry_index() and follow ups:
  avs_get_module_entry(), avs_get_module_id_entry() to better conform to
  linux-kernel standard when no entry is found (return -ENOENT) rather
  than C++ standard (return -1, what in kernel case translated to -EPERM)
  as suggested by Curtis and Peter
- several suggestions have been made regarding spacing, and so far, I've
  agreed and applied with all of them. None proposed seemed out of place
  or redundant

- avs_path_stop() renamed to avs_path_pause() pipeline states are
  represented by RESET/PAUSED/RUNNING. avs_path_reset() and
  avs_path_run() were already there and avs_path_stop() just didn't look
  cohesive
- added missing parsers for num_output_pin and num_input_pin which are
  required for custom modules such as WAVES or DSM
- dropped 'priv_param_length' from custom module descriptor as this
  field is obsolete in firmware

- parse_dictionary() has been split into parse_dictionary_header() and
  parse_dictionary_entries() to drop code duplication present in several
  parsing function which could not re-use entire parse_dictionary()
- added avs_tplg_vendor_array_lookup_next() and
  avs_tplg_vendor_entry_next() to drop code duplicated present in several
  parsing functions. This change greatly impacted readability of all
  parsers
- parsing helpers such avs_tplg_vendor_array_lookup() now return offset
  by updating specified in function argument list u32 *offset variable.
  This is to address problem when u32 offset would be greater than max
  int, which is the return type for these functions
- AVS_DEFINE_PTR_PARSER() macro has been introduced to drop code
  duplication for all ptr-parsing users

- all struct avs_path_module creators have had their declaration
  updated: function argument *owner ceased to exist as it could already
  be accessed by mod->owner

- fixed the order of operation for conditional paths (e.g.: echo
  reference) so these are no longer controlled by "source" path and
  instead are impacted by state changes of source and sink paths both.
  Previously only source path e.g. playback sourcing echo reference
  would trigger RUNNING status for conditional path. Equivalent RUNNING
  on WoV path which is in this case sink path, would not do so, leading
  to order-of-operation problems. Behavior has been changed to: both
  source and sink need to be RUNNING for conditional path to be set to
  RUNNING too. PAUSED for either source or sink will cause PAUSED
  transition for conditional path.
- to achieve the above, path states are now saved in 'state' i.e. new
  u32 field for struct avs_path

- resigned from fw_filename field usage in favour of newly added
  tplg_filename for machine board descriptors as suggested by Pierre
- platform descriptor fields have had their names update better reflect
  their purpose as suggested by Pierre
- fixed comp_list missing locking when manipulated
- all message senders now accept request as pointer as suggeseted by
  Peter
- resigned of AZX_ usage for all ADSP-related registers, leaving them
  only for HOST memory space related operations
- fixed disable path for core DSP operations: power/reset/stall as
  reported by Peter

- safety when locking between received responses (reply vs notification)
  has been lowered as suggested by Pierre. Most usages are not performed
  in IRQ context and none is done in hard-IRQ one
- s/master/main/ plus AVS_MAIN_CORE_MASK has replaced ->master_mask
- several functions have had their logging updated - logs have been
  moved to lower level functions as suggested by Pierre
- hdac_ext_stream usage has been streamlined to estream, hdac_streams
  are represented by hstream instead
- hw_params() are resilient to scenarios when they are called mutliple
  times as reported by Pierre
- avs_dsp_enable() now collapses if any of its steps fails as reported
  by Pierre and Peter
- avs_module_ida_empty() now returns value of type bool as suggested by
  Bard


[1]: https://www.spinics.net/lists/alsa-devel/msg116440.html
[2]: https://www.spinics.net/lists/alsa-devel/msg116901.html
[3]: https://www.spinics.net/lists/alsa-devel/msg94199.html
[4]: https://www.spinics.net/lists/alsa-devel/msg92588.html
[5]: https://lore.kernel.org/all/20190808181549.12521-1-cezary.rojewski@intel.com/
[6]: https://lore.kernel.org/alsa-devel/20220207114906.3759800-1-cezary.rojewski@intel.com/T/#t
[7]: https://lore.kernel.org/all/20211208111301.1817725-1-cezary.rojewski@intel.com/


Cezary Rojewski (17):
  ALSA: hda: Add helper macros for DSP capable devices
  ASoC: Export DAI register and widget ctor and dctor functions
  ASoC: Intel: Introduce AVS driver
  ASoC: Intel: avs: Inter process communication
  ASoC: Intel: avs: Add code loading requests
  ASoC: Intel: avs: Add pipeline management requests
  ASoC: Intel: avs: Add module management requests
  ASoC: Intel: avs: Add power management requests
  ASoC: Intel: avs: Add ROM requests
  ASoC: Intel: avs: Add basefw runtime-parameter requests
  ASoC: Intel: avs: Firmware resources management utilities
  ASoC: Intel: avs: Declare module configuration types
  ASoC: Intel: avs: Dynamic firmware resources management
  ASoC: Intel: avs: General code loading flow
  ASoC: Intel: avs: Implement CLDMA transfer
  ASoC: Intel: avs: Code loading over CLDMA
  ASoC: Intel: avs: Code loading over HDA

 include/sound/hdaudio.h         |   2 +
 include/sound/hdaudio_ext.h     |  49 ++
 include/sound/soc-dapm.h        |   1 +
 sound/soc/intel/Kconfig         |  15 +
 sound/soc/intel/Makefile        |   1 +
 sound/soc/intel/avs/Makefile    |   6 +
 sound/soc/intel/avs/avs.h       | 226 ++++++++++
 sound/soc/intel/avs/cldma.c     | 328 ++++++++++++++
 sound/soc/intel/avs/cldma.h     |  29 ++
 sound/soc/intel/avs/core.c      |  62 +++
 sound/soc/intel/avs/dsp.c       | 303 +++++++++++++
 sound/soc/intel/avs/ipc.c       | 410 +++++++++++++++++
 sound/soc/intel/avs/loader.c    | 594 +++++++++++++++++++++++++
 sound/soc/intel/avs/messages.c  | 642 +++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h  | 762 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/registers.h |  75 ++++
 sound/soc/intel/avs/utils.c     | 282 ++++++++++++
 sound/soc/soc-core.c            |   1 +
 sound/soc/soc-dapm.c            |  15 +
 19 files changed, 3803 insertions(+)
 create mode 100644 sound/soc/intel/avs/Makefile
 create mode 100644 sound/soc/intel/avs/avs.h
 create mode 100644 sound/soc/intel/avs/cldma.c
 create mode 100644 sound/soc/intel/avs/cldma.h
 create mode 100644 sound/soc/intel/avs/core.c
 create mode 100644 sound/soc/intel/avs/dsp.c
 create mode 100644 sound/soc/intel/avs/ipc.c
 create mode 100644 sound/soc/intel/avs/loader.c
 create mode 100644 sound/soc/intel/avs/messages.c
 create mode 100644 sound/soc/intel/avs/messages.h
 create mode 100644 sound/soc/intel/avs/registers.h
 create mode 100644 sound/soc/intel/avs/utils.c

-- 
2.25.1


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

* [PATCH 01/17] ALSA: hda: Add helper macros for DSP capable devices
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 02/17] ASoC: Export DAI register and widget ctor and dctor functions Cezary Rojewski
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

HDAudio drivers make heavy use of I/O operations. Declare a range of
update, read and write helpers similar to those available for HDAudio
legacy driver. These macros are used by AVS driver to improve code
readability.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 include/sound/hdaudio.h     |  2 ++
 include/sound/hdaudio_ext.h | 49 +++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index 6a90ce405e60..69907260b9ce 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -448,6 +448,8 @@ static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
 
 #define snd_hdac_reg_writel(bus, addr, val)	writel(val, addr)
 #define snd_hdac_reg_readl(bus, addr)	readl(addr)
+#define snd_hdac_reg_writeq(bus, addr, val)	writeq(val, addr)
+#define snd_hdac_reg_readq(bus, addr)		readq(addr)
 
 /*
  * macros for easy use
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h
index b0c8e4936168..8c06c4a3bcd2 100644
--- a/include/sound/hdaudio_ext.h
+++ b/include/sound/hdaudio_ext.h
@@ -2,6 +2,7 @@
 #ifndef __SOUND_HDAUDIO_EXT_H
 #define __SOUND_HDAUDIO_EXT_H
 
+#include <linux/iopoll.h>
 #include <sound/hdaudio.h>
 
 int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
@@ -144,6 +145,54 @@ void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable);
 	writew(((readw(addr + reg) & ~(mask)) | (val)), \
 		addr + reg)
 
+#define snd_hdac_adsp_writeb(chip, reg, value) \
+	snd_hdac_reg_writeb(chip, (chip)->adsp_ba + (reg), value)
+#define snd_hdac_adsp_readb(chip, reg) \
+	snd_hdac_reg_readb(chip, (chip)->adsp_ba + (reg))
+#define snd_hdac_adsp_writew(chip, reg, value) \
+	snd_hdac_reg_writew(chip, (chip)->adsp_ba + (reg), value)
+#define snd_hdac_adsp_readw(chip, reg) \
+	snd_hdac_reg_readw(chip, (chip)->adsp_ba + (reg))
+#define snd_hdac_adsp_writel(chip, reg, value) \
+	snd_hdac_reg_writel(chip, (chip)->adsp_ba + (reg), value)
+#define snd_hdac_adsp_readl(chip, reg) \
+	snd_hdac_reg_readl(chip, (chip)->adsp_ba + (reg))
+#define snd_hdac_adsp_writeq(chip, reg, value) \
+	snd_hdac_reg_writeq(chip, (chip)->adsp_ba + (reg), value)
+#define snd_hdac_adsp_readq(chip, reg) \
+	snd_hdac_reg_readq(chip, (chip)->adsp_ba + (reg))
+
+#define snd_hdac_adsp_updateb(chip, reg, mask, val) \
+	snd_hdac_adsp_writeb(chip, reg, \
+			(snd_hdac_adsp_readb(chip, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updatew(chip, reg, mask, val) \
+	snd_hdac_adsp_writew(chip, reg, \
+			(snd_hdac_adsp_readw(chip, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updatel(chip, reg, mask, val) \
+	snd_hdac_adsp_writel(chip, reg, \
+			(snd_hdac_adsp_readl(chip, reg) & ~(mask)) | (val))
+#define snd_hdac_adsp_updateq(chip, reg, mask, val) \
+	snd_hdac_adsp_writeq(chip, reg, \
+			(snd_hdac_adsp_readq(chip, reg) & ~(mask)) | (val))
+
+#define snd_hdac_adsp_readb_poll(chip, reg, val, cond, delay_us, timeout_us) \
+	readb_poll_timeout((chip)->adsp_ba + (reg), val, cond, \
+			   delay_us, timeout_us)
+#define snd_hdac_adsp_readw_poll(chip, reg, val, cond, delay_us, timeout_us) \
+	readw_poll_timeout((chip)->adsp_ba + (reg), val, cond, \
+			   delay_us, timeout_us)
+#define snd_hdac_adsp_readl_poll(chip, reg, val, cond, delay_us, timeout_us) \
+	readl_poll_timeout((chip)->adsp_ba + (reg), val, cond, \
+			   delay_us, timeout_us)
+#define snd_hdac_adsp_readq_poll(chip, reg, val, cond, delay_us, timeout_us) \
+	readq_poll_timeout((chip)->adsp_ba + (reg), val, cond, \
+			   delay_us, timeout_us)
+#define snd_hdac_stream_readb_poll(strm, reg, val, cond, delay_us, timeout_us) \
+	readb_poll_timeout((strm)->sd_addr + AZX_REG_ ## reg, val, cond, \
+			   delay_us, timeout_us)
+#define snd_hdac_stream_readl_poll(strm, reg, val, cond, delay_us, timeout_us) \
+	readl_poll_timeout((strm)->sd_addr + AZX_REG_ ## reg, val, cond, \
+			   delay_us, timeout_us)
 
 struct hdac_ext_device;
 
-- 
2.25.1


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

* [PATCH 02/17] ASoC: Export DAI register and widget ctor and dctor functions
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 01/17] ALSA: hda: Add helper macros for DSP capable devices Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 03/17] ASoC: Intel: Introduce AVS driver Cezary Rojewski
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

To allow for more flexibility i.e. populating component DAIs dynamically
during its initialization, without being limited to topology loading
procedure, expose snd_soc_register(), snd_soc_dapm_new_dai_widgets() and
snd_soc_dapm_free_widget() functions.

Allows users to first check available resources e.g. number of PCMs
supported by HDAudio codec before allocating the number of DAPM
widgets needed. This prevents superfluous objects from being created or
allows driver to adjust to situation when resources are limited.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 include/sound/soc-dapm.h |  1 +
 sound/soc/soc-core.c     |  1 +
 sound/soc/soc-dapm.c     | 15 +++++++++++++++
 3 files changed, 17 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c3039e97929a..ebb8e7a7fc29 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -429,6 +429,7 @@ struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(
 		const struct snd_soc_dapm_widget *widget);
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 				 struct snd_soc_dai *dai);
+void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w);
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 259429526c84..1f0564ed3a75 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2465,6 +2465,7 @@ struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
 	dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
 	return dai;
 }
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
 
 /**
  * snd_soc_unregister_dais - Unregister DAIs from the ASoC core
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b06c5682445c..b435b5c4cfb7 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2484,6 +2484,12 @@ static void dapm_free_path(struct snd_soc_dapm_path *path)
 	kfree(path);
 }
 
+/**
+ * snd_soc_dapm_free_widget - Free specified widget
+ * @w: widget to free
+ *
+ * Removes widget from all paths and frees memory occupied by it.
+ */
 void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
 {
 	struct snd_soc_dapm_path *p, *next_p;
@@ -2506,6 +2512,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
 	kfree_const(w->sname);
 	kfree(w);
 }
+EXPORT_SYMBOL_GPL(snd_soc_dapm_free_widget);
 
 void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm)
 {
@@ -4208,6 +4215,13 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
 	return ERR_PTR(ret);
 }
 
+/**
+ * snd_soc_dapm_new_dai_widgets - Create new DAPM widgets
+ * @dapm: DAPM context
+ * @dai: parent DAI
+ *
+ * Returns 0 on success, error code otherwise.
+ */
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 				 struct snd_soc_dai *dai)
 {
@@ -4253,6 +4267,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_dai_widgets);
 
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
 {
-- 
2.25.1


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

* [PATCH 03/17] ASoC: Intel: Introduce AVS driver
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 01/17] ALSA: hda: Add helper macros for DSP capable devices Cezary Rojewski
  2022-02-07 12:20 ` [PATCH 02/17] ASoC: Export DAI register and widget ctor and dctor functions Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-24 23:55   ` Pierre-Louis Bossart
  2022-02-07 12:20 ` [PATCH 04/17] ASoC: Intel: avs: Inter process communication Cezary Rojewski
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Declare base structures and core DSP operations for the avs solution.
The base structures describe PCI HDAudio bus device and platform-type
differentiations. First set of operations added controls the lifecycle
of any Audio DSP core: (un)powering, (un)resetting and (un)stalling.

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         |  14 +++++
 sound/soc/intel/Makefile        |   1 +
 sound/soc/intel/avs/Makefile    |   5 ++
 sound/soc/intel/avs/avs.h       |  64 +++++++++++++++++++
 sound/soc/intel/avs/dsp.c       | 107 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/registers.h |  22 +++++++
 6 files changed, 213 insertions(+)
 create mode 100644 sound/soc/intel/avs/Makefile
 create mode 100644 sound/soc/intel/avs/avs.h
 create mode 100644 sound/soc/intel/avs/dsp.c
 create mode 100644 sound/soc/intel/avs/registers.h

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f3a4a907b29d..5b4941d88101 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -209,5 +209,19 @@ config SND_SOC_INTEL_KEEMBAY
 	  If you have a Intel Keembay platform then enable this option
 	  by saying Y or m.
 
+config SND_SOC_INTEL_AVS
+	tristate "Intel AVS driver"
+	depends on PCI && ACPI
+	depends on COMMON_CLK
+	depends on SND_SOC_INTEL_SKYLAKE_FAMILY=n
+	default n
+	select SND_SOC_ACPI
+	select SND_HDA_EXT_CORE
+	help
+	  Enable support for Intel(R) cAVS 1.5 platforms with DSP
+	  capabilities. This includes Skylake, Kabylake, Amberlake and
+	  Apollolake. This option is mutually exclusive with SKYLAKE
+	  driver.
+
 # ASoC codec drivers
 source "sound/soc/intel/boards/Kconfig"
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index 7c5038803be7..d44b2652c707 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/
 obj-$(CONFIG_SND_SOC_INTEL_CATPT) += catpt/
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_COMMON) += skylake/
 obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += keembay/
+obj-$(CONFIG_SND_SOC_INTEL_AVS) += avs/
 
 # Machine support
 obj-$(CONFIG_SND_SOC) += boards/
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
new file mode 100644
index 000000000000..5f7976a95fe2
--- /dev/null
+++ b/sound/soc/intel/avs/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+snd-soc-avs-objs := dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
new file mode 100644
index 000000000000..7ece210b0777
--- /dev/null
+++ b/sound/soc/intel/avs/avs.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_H
+#define __SOUND_SOC_INTEL_AVS_H
+
+#include <linux/device.h>
+#include <sound/hda_codec.h>
+
+struct avs_dev;
+
+struct avs_dsp_ops {
+	int (* const power)(struct avs_dev *, u32, bool);
+	int (* const reset)(struct avs_dev *, u32, bool);
+	int (* const stall)(struct avs_dev *, u32, bool);
+};
+
+#define avs_dsp_op(adev, op, ...) \
+	((adev)->spec->dops->op(adev, ## __VA_ARGS__))
+
+#define avs_platattr_test(adev, attr) \
+	((adev)->spec->attributes & AVS_PLATATTR_##attr)
+
+/* Platform specific descriptor */
+struct avs_spec {
+	const char *name;
+
+	const struct avs_dsp_ops *const dops;
+
+	const u32 core_init_mask;	/* used during DSP boot */
+	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
+};
+
+struct avs_dev {
+	struct hda_bus base;
+	struct device *dev;
+
+	void __iomem *adsp_ba;
+	const struct avs_spec *spec;
+};
+
+/* from hda_bus to avs_dev */
+#define hda_to_avs(hda) container_of(hda, struct avs_dev, base)
+/* from hdac_bus to avs_dev */
+#define hdac_to_avs(hdac) hda_to_avs(to_hda_bus(hdac))
+/* from device to avs_dev */
+#define to_avs_dev(dev) \
+({ \
+	struct hdac_bus *__bus = dev_get_drvdata(dev); \
+	hdac_to_avs(__bus); \
+})
+
+int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active);
+int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset);
+int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
+int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
+
+#endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
new file mode 100644
index 000000000000..258544277bbb
--- /dev/null
+++ b/sound/soc/intel/avs/dsp.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+
+#define AVS_ADSPCS_INTERVAL_US		500
+#define AVS_ADSPCS_TIMEOUT_US		10000
+
+int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active)
+{
+	u32 value, mask, reg;
+	int ret;
+
+	mask = AVS_ADSPCS_SPA_MASK(core_mask);
+	value = active ? mask : 0;
+
+	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+
+	mask = AVS_ADSPCS_CPA_MASK(core_mask);
+	value = active ? mask : 0;
+
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+				       reg, (reg & mask) == value,
+				       AVS_ADSPCS_INTERVAL_US,
+				       AVS_ADSPCS_TIMEOUT_US);
+	if (ret)
+		dev_err(adev->dev, "core_mask %d %spower failed: %d\n",
+			core_mask, active ? "" : "un", ret);
+
+	return ret;
+}
+
+int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
+{
+	u32 value, mask, reg;
+	int ret;
+
+	mask = AVS_ADSPCS_CRST_MASK(core_mask);
+	value = reset ? mask : 0;
+
+	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+				       reg, (reg & mask) == value,
+				       AVS_ADSPCS_INTERVAL_US,
+				       AVS_ADSPCS_TIMEOUT_US);
+	if (ret)
+		dev_err(adev->dev, "core_mask %d %sreset failed: %d\n",
+			core_mask, reset ? "" : "un", ret);
+
+	return ret;
+}
+
+int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+	u32 value, mask, reg;
+	int ret;
+
+	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
+	value = stall ? mask : 0;
+
+	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
+
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
+				       reg, (reg & mask) == value,
+				       AVS_ADSPCS_INTERVAL_US,
+				       AVS_ADSPCS_TIMEOUT_US);
+	if (ret)
+		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
+			core_mask, stall ? "" : "un", ret);
+
+	return ret;
+}
+
+int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
+{
+	int ret;
+
+	ret = avs_dsp_op(adev, power, core_mask, true);
+	if (ret)
+		return ret;
+
+	ret = avs_dsp_op(adev, reset, core_mask, false);
+	if (ret)
+		return ret;
+
+	return avs_dsp_op(adev, stall, core_mask, false);
+}
+
+int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
+{
+	/* Be permissive to allow for full DSP shutdown in disable path. */
+	avs_dsp_op(adev, stall, core_mask, true);
+	avs_dsp_op(adev, reset, core_mask, true);
+
+	return avs_dsp_op(adev, power, core_mask, false);
+}
+
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
new file mode 100644
index 000000000000..e0b6c8ffe633
--- /dev/null
+++ b/sound/soc/intel/avs/registers.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_REGS_H
+#define __SOUND_SOC_INTEL_AVS_REGS_H
+
+/* Intel HD Audio General DSP Registers */
+#define AVS_ADSP_GEN_BASE		0x0
+#define AVS_ADSP_REG_ADSPCS		(AVS_ADSP_GEN_BASE + 0x04)
+
+#define AVS_ADSPCS_CRST_MASK(cm)	(cm)
+#define AVS_ADSPCS_CSTALL_MASK(cm)	((cm) << 8)
+#define AVS_ADSPCS_SPA_MASK(cm)		((cm) << 16)
+#define AVS_ADSPCS_CPA_MASK(cm)		((cm) << 24)
+#define AVS_MAIN_CORE_MASK		BIT(0)
+
+#endif /* __SOUND_SOC_INTEL_AVS_REGS_H */
-- 
2.25.1


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

* [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (2 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 03/17] ASoC: Intel: Introduce AVS driver Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-25  0:56   ` Pierre-Louis Bossart
  2022-02-07 12:20 ` [PATCH 05/17] ASoC: Intel: avs: Add code loading requests Cezary Rojewski
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Implement the IPC between Intel audio firmware and kernel driver. The
IPC allows transmission of requests, handling of responses as well as
unsolicited (i.e. firmware-generated) notifications.

A subscription mechanism is added to enable different parts of the
driver to register for specific notifications.

The boot process involving ROM-code requires specific handling with
'unstall' operations which are not required post-boot with normal IPC so
separate set of send-message handlers is added for each of the usecases.

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       |  93 ++++++++
 sound/soc/intel/avs/ipc.c       | 404 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h  | 170 ++++++++++++++
 sound/soc/intel/avs/registers.h |  45 ++++
 5 files changed, 713 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/ipc.c
 create mode 100644 sound/soc/intel/avs/messages.h

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 5f7976a95fe2..e243806dd38a 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-snd-soc-avs-objs := dsp.o
+snd-soc-avs-objs := dsp.o ipc.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 7ece210b0777..8620d2f7fee0 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -11,6 +11,7 @@
 
 #include <linux/device.h>
 #include <sound/hda_codec.h>
+#include "messages.h"
 
 struct avs_dev;
 
@@ -18,6 +19,9 @@ struct avs_dsp_ops {
 	int (* const power)(struct avs_dev *, u32, bool);
 	int (* const reset)(struct avs_dev *, u32, bool);
 	int (* const stall)(struct avs_dev *, u32, bool);
+	irqreturn_t (* const irq_handler)(int, void *);
+	irqreturn_t (* const irq_thread)(int, void *);
+	void (* const int_control)(struct avs_dev *, bool);
 };
 
 #define avs_dsp_op(adev, op, ...) \
@@ -34,6 +38,18 @@ struct avs_spec {
 
 	const u32 core_init_mask;	/* used during DSP boot */
 	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
+	const u32 sram_base_offset;
+	const u32 sram_window_size;
+
+	const u32 rom_status;
+	const u32 hipc_req_offset;
+	const u32 hipc_req_ext_offset;
+	const u32 hipc_req_busy_mask;
+	const u32 hipc_ack_offset;
+	const u32 hipc_ack_done_mask;
+	const u32 hipc_rsp_offset;
+	const u32 hipc_rsp_busy_mask;
+	const u32 hipc_ctl_offset;
 };
 
 struct avs_dev {
@@ -42,6 +58,9 @@ struct avs_dev {
 
 	void __iomem *adsp_ba;
 	const struct avs_spec *spec;
+	struct avs_ipc *ipc;
+
+	struct completion fw_ready;
 };
 
 /* from hda_bus to avs_dev */
@@ -61,4 +80,78 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
 int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
 int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
 
+/* Inter Process Communication */
+
+struct avs_ipc_msg {
+	union {
+		u64 header;
+		union avs_global_msg glb;
+		union avs_reply_msg rsp;
+	};
+	void *data;
+	size_t size;
+};
+
+struct avs_ipc {
+	struct device *dev;
+
+	struct avs_ipc_msg rx;
+	u32 default_timeout_ms;
+	bool ready;
+
+	bool rx_completed;
+	spinlock_t rx_lock;
+	struct mutex msg_mutex;
+	struct completion done_completion;
+	struct completion busy_completion;
+};
+
+#define AVS_EIPC	EREMOTEIO
+/*
+ * IPC handlers may return positive value (firmware error code) what denotes
+ * successful HOST <-> DSP communication yet failure to process specific request.
+ *
+ * Below macro converts returned value to linux kernel error code.
+ * All IPC callers MUST use it as soon as firmware error code is consumed.
+ */
+#define AVS_IPC_RET(ret) \
+	(((ret) <= 0) ? (ret) : -AVS_EIPC)
+
+static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx,
+			       const char *name, int error)
+{
+	/*
+	 * If IPC channel is blocked e.g.: due to ongoing recovery,
+	 * -EPERM error code is expected and thus it's not an actual error.
+	 */
+	if (error == -EPERM)
+		dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
+			tx->glb.primary, tx->glb.ext.val, error);
+	else
+		dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
+			tx->glb.primary, tx->glb.ext.val, error);
+}
+
+irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id);
+irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id);
+void avs_dsp_process_response(struct avs_dev *adev, u64 header);
+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_msg_timeout(struct avs_dev *adev,
+			     struct avs_ipc_msg *request,
+			     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);
+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);
+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);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
new file mode 100644
index 000000000000..69178b5d39b1
--- /dev/null
+++ b/sound/soc/intel/avs/ipc.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "messages.h"
+#include "registers.h"
+
+#define AVS_IPC_TIMEOUT_MS	300
+
+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);
+
+	ipc->rx.header = header;
+	if (!msg.status)
+		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
+			      ipc->rx.size);
+}
+
+static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
+{
+	struct avs_notify_mod_data mod_data;
+	union avs_notify_msg msg = AVS_MSG(header);
+	size_t data_size = 0;
+	void *data = NULL;
+
+	if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
+		dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n",
+			msg.primary);
+		return;
+	}
+
+	/* Calculate notification payload size. */
+	switch (msg.notify_msg_type) {
+	case AVS_NOTIFY_FW_READY:
+		break;
+
+	case AVS_NOTIFY_PHRASE_DETECTED:
+		data_size = sizeof(struct avs_notify_voice_data);
+		break;
+
+	case AVS_NOTIFY_RESOURCE_EVENT:
+		data_size = sizeof(struct avs_notify_res_data);
+		break;
+
+	case AVS_NOTIFY_MODULE_EVENT:
+		memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
+		data_size = sizeof(mod_data) + mod_data.data_size;
+		break;
+
+	default:
+		dev_warn(adev->dev, "unknown notification: 0x%08x\n",
+			 msg.primary);
+		break;
+	}
+
+	if (data_size) {
+		data = kmalloc(data_size, GFP_KERNEL);
+		if (!data)
+			return;
+
+		memcpy_fromio(data, avs_uplink_addr(adev), data_size);
+	}
+
+	/* Perform notification-specific operations. */
+	switch (msg.notify_msg_type) {
+	case AVS_NOTIFY_FW_READY:
+		dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
+		adev->ipc->ready = true;
+		complete(&adev->fw_ready);
+		break;
+
+	default:
+		break;
+	}
+
+	kfree(data);
+}
+
+void avs_dsp_process_response(struct avs_dev *adev, u64 header)
+{
+	struct avs_ipc *ipc = adev->ipc;
+
+	if (avs_msg_is_reply(header)) {
+		/* Response processing is invoked from IRQ thread. */
+		spin_lock_irq(&ipc->rx_lock);
+		avs_dsp_receive_rx(adev, header);
+		ipc->rx_completed = true;
+		spin_unlock_irq(&ipc->rx_lock);
+	} else {
+		avs_dsp_process_notification(adev, header);
+	}
+
+	complete(&ipc->busy_completion);
+}
+
+irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
+{
+	struct avs_dev *adev = dev_id;
+	struct avs_ipc *ipc = adev->ipc;
+	const struct avs_spec *const spec = adev->spec;
+	u32 adspis, hipc_rsp, hipc_ack;
+	irqreturn_t ret = IRQ_NONE;
+
+	adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
+	if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC))
+		return ret;
+
+	hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc_ack_offset);
+	hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc_rsp_offset);
+
+	/* DSP acked host's request */
+	if (hipc_ack & spec->hipc_ack_done_mask) {
+		/* mask done interrupt */
+		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
+				      AVS_ADSP_HIPCCTL_DONE, 0);
+
+		complete(&ipc->done_completion);
+
+		/* tell DSP it has our attention */
+		snd_hdac_adsp_updatel(adev, spec->hipc_ack_offset,
+				      spec->hipc_ack_done_mask,
+				      spec->hipc_ack_done_mask);
+		/* unmask done interrupt */
+		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
+				      AVS_ADSP_HIPCCTL_DONE,
+				      AVS_ADSP_HIPCCTL_DONE);
+		ret = IRQ_HANDLED;
+	}
+
+	/* DSP sent new response to process */
+	if (hipc_rsp & spec->hipc_rsp_busy_mask) {
+		/* mask busy interrupt */
+		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
+				      AVS_ADSP_HIPCCTL_BUSY, 0);
+
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	return ret;
+}
+
+irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id)
+{
+	struct avs_dev *adev = dev_id;
+	union avs_reply_msg msg;
+	u32 hipct, hipcte;
+
+	hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT);
+	hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE);
+
+	/* ensure DSP sent new response to process */
+	if (!(hipct & SKL_ADSP_HIPCT_BUSY))
+		return IRQ_NONE;
+
+	msg.primary = hipct;
+	msg.ext.val = hipcte;
+	avs_dsp_process_response(adev, msg.val);
+
+	/* tell DSP we accepted its message */
+	snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT,
+			      SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY);
+	/* unmask busy interrupt */
+	snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL,
+			      AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY);
+
+	return IRQ_HANDLED;
+}
+
+static bool avs_ipc_is_busy(struct avs_ipc *ipc)
+{
+	struct avs_dev *adev = to_avs_dev(ipc->dev);
+	const struct avs_spec *const spec = adev->spec;
+	u32 hipc_rsp;
+
+	hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc_rsp_offset);
+	return hipc_rsp & spec->hipc_rsp_busy_mask;
+}
+
+static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
+{
+	int ret;
+
+again:
+	ret = wait_for_completion_timeout(&ipc->busy_completion,
+					  msecs_to_jiffies(timeout));
+	/*
+	 * DSP could be unresponsive at this point e.g. manifested by
+	 * EXCEPTION_CAUGHT notification. If so, no point in continuing.
+	 */
+	if (!ipc->ready)
+		return -EPERM;
+
+	if (!ret) {
+		if (!avs_ipc_is_busy(ipc))
+			return -ETIMEDOUT;
+		/*
+		 * Firmware did its job, either notification or reply
+		 * has been received - now wait until it's processed.
+		 */
+		wait_for_completion_killable(&ipc->busy_completion);
+	}
+
+	/* Ongoing notification's bottom-half may cause early wakeup */
+	spin_lock(&ipc->rx_lock);
+	if (!ipc->rx_completed) {
+		/* Reply delayed due to notification. */
+		reinit_completion(&ipc->busy_completion);
+		spin_unlock(&ipc->rx_lock);
+		goto again;
+	}
+
+	spin_unlock(&ipc->rx_lock);
+	return 0;
+}
+
+static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply)
+{
+	lockdep_assert_held(&ipc->rx_lock);
+
+	ipc->rx.header = 0;
+	ipc->rx.size = reply ? reply->size : 0;
+	ipc->rx_completed = false;
+
+	reinit_completion(&ipc->done_completion);
+	reinit_completion(&ipc->busy_completion);
+}
+
+static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx)
+{
+	const struct avs_spec *const spec = adev->spec;
+
+	tx->header |= spec->hipc_req_busy_mask;
+
+	if (tx->size)
+		memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size);
+	snd_hdac_adsp_writel(adev, spec->hipc_req_ext_offset, tx->header >> 32);
+	snd_hdac_adsp_writel(adev, spec->hipc_req_offset, tx->header & UINT_MAX);
+}
+
+static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+			       struct avs_ipc_msg *reply, int timeout)
+{
+	struct avs_ipc *ipc = adev->ipc;
+	int ret;
+
+	if (!ipc->ready)
+		return -EPERM;
+
+	mutex_lock(&ipc->msg_mutex);
+
+	spin_lock(&ipc->rx_lock);
+	avs_ipc_msg_init(ipc, reply);
+	avs_dsp_send_tx(adev, request);
+	spin_unlock(&ipc->rx_lock);
+
+	ret = avs_ipc_wait_busy_completion(ipc, timeout);
+	if (ret) {
+		if (ret == -ETIMEDOUT) {
+			dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n",
+				 ret);
+
+			avs_ipc_block(ipc);
+		}
+		goto exit;
+	}
+
+	ret = ipc->rx.rsp.status;
+	if (reply) {
+		reply->header = ipc->rx.header;
+		if (reply->data && ipc->rx.size)
+			memcpy(reply->data, ipc->rx.data, reply->size);
+	}
+
+exit:
+	mutex_unlock(&ipc->msg_mutex);
+	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)
+{
+	return avs_dsp_do_send_msg(adev, request, reply, timeout);
+}
+
+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_send_msg_sequence(adev, request, reply, timeout,
+					 false, false);
+}
+
+int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
+		     struct avs_ipc_msg *reply)
+{
+	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;
+	int ret;
+
+	mutex_lock(&ipc->msg_mutex);
+
+	spin_lock(&ipc->rx_lock);
+	avs_ipc_msg_init(ipc, NULL);
+	avs_dsp_send_tx(adev, request);
+	spin_unlock(&ipc->rx_lock);
+
+	/* ROM messages must be sent before main core is unstalled */
+	ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false);
+	if (!ret) {
+		ret = wait_for_completion_timeout(&ipc->done_completion,
+						  msecs_to_jiffies(timeout));
+		ret = ret ? 0 : -ETIMEDOUT;
+	}
+
+	mutex_unlock(&ipc->msg_mutex);
+
+	return ret;
+}
+
+int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev,
+				 struct avs_ipc_msg *request, int timeout)
+{
+	return avs_dsp_do_send_rom_msg(adev, request, timeout);
+}
+
+int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request)
+{
+	return avs_dsp_send_rom_msg_timeout(adev, request,
+					    adev->ipc->default_timeout_ms);
+}
+
+void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
+{
+	const struct avs_spec *const spec = adev->spec;
+	u32 value;
+
+	value = enable ? AVS_ADSP_ADSPIC_IPC : 0;
+	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC,
+			      AVS_ADSP_ADSPIC_IPC, value);
+
+	value = enable ? AVS_ADSP_HIPCCTL_DONE : 0;
+	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
+			      AVS_ADSP_HIPCCTL_DONE, value);
+
+	value = enable ? AVS_ADSP_HIPCCTL_BUSY : 0;
+	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
+			      AVS_ADSP_HIPCCTL_BUSY, value);
+}
+
+int avs_ipc_init(struct avs_ipc *ipc, struct device *dev)
+{
+	ipc->rx.data = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
+	if (!ipc->rx.data)
+		return -ENOMEM;
+
+	ipc->dev = dev;
+	ipc->ready = false;
+	ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS;
+	init_completion(&ipc->done_completion);
+	init_completion(&ipc->busy_completion);
+	spin_lock_init(&ipc->rx_lock);
+	mutex_init(&ipc->msg_mutex);
+
+	return 0;
+}
+
+void avs_ipc_block(struct avs_ipc *ipc)
+{
+	ipc->ready = false;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
new file mode 100644
index 000000000000..003e634f5547
--- /dev/null
+++ b/sound/soc/intel/avs/messages.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_MSGS_H
+#define __SOUND_SOC_INTEL_AVS_MSGS_H
+
+struct avs_dev;
+
+#define AVS_MAILBOX_SIZE 4096
+
+enum avs_msg_target {
+	AVS_FW_GEN_MSG = 0,
+	AVS_MOD_MSG = 1
+};
+
+enum avs_msg_direction {
+	AVS_MSG_REQUEST = 0,
+	AVS_MSG_REPLY = 1
+};
+
+enum avs_global_msg_type {
+	AVS_GLB_NOTIFICATION = 27,
+};
+
+union avs_global_msg {
+	u64 val;
+	struct {
+		union {
+			u32 primary;
+			struct {
+				u32 rsvd:24;
+				u32 global_msg_type:5;
+				u32 msg_direction:1;
+				u32 msg_target:1;
+			};
+		};
+		union {
+			u32 val;
+		} ext;
+	};
+} __packed;
+
+struct avs_tlv {
+	u32 type;
+	u32 length;
+	u32 value[];
+} __packed;
+
+union avs_module_msg {
+	u64 val;
+	struct {
+		union {
+			u32 primary;
+			struct {
+				u32 module_id:16;
+				u32 instance_id:8;
+				u32 module_msg_type:5;
+				u32 msg_direction:1;
+				u32 msg_target:1;
+			};
+		};
+		union {
+			u32 val;
+		} ext;
+	};
+} __packed;
+
+union avs_reply_msg {
+	u64 val;
+	struct {
+		union {
+			u32 primary;
+			struct {
+				u32 status:24;
+				u32 global_msg_type:5;
+				u32 msg_direction:1;
+				u32 msg_target:1;
+			};
+		};
+		union {
+			u32 val;
+		} ext;
+	};
+} __packed;
+
+enum avs_notify_msg_type {
+	AVS_NOTIFY_PHRASE_DETECTED = 4,
+	AVS_NOTIFY_RESOURCE_EVENT = 5,
+	AVS_NOTIFY_FW_READY = 8,
+	AVS_NOTIFY_MODULE_EVENT = 12,
+};
+
+union avs_notify_msg {
+	u64 val;
+	struct {
+		union {
+			u32 primary;
+			struct {
+				u32 rsvd:16;
+				u32 notify_msg_type:8;
+				u32 global_msg_type:5;
+				u32 msg_direction:1;
+				u32 msg_target:1;
+			};
+		};
+		union {
+			u32 val;
+		} ext;
+	};
+} __packed;
+
+#define AVS_MSG(hdr) { .val = hdr }
+
+#define AVS_GLOBAL_REQUEST(msg_type)		\
+{						\
+	.global_msg_type = AVS_GLB_##msg_type,	\
+	.msg_direction = AVS_MSG_REQUEST,	\
+	.msg_target = AVS_FW_GEN_MSG,		\
+}
+
+#define AVS_MODULE_REQUEST(msg_type)		\
+{						\
+	.module_msg_type = AVS_MOD_##msg_type,	\
+	.msg_direction = AVS_MSG_REQUEST,	\
+	.msg_target = AVS_MOD_MSG,		\
+}
+
+#define AVS_NOTIFICATION(msg_type)		\
+{						\
+	.notify_msg_type = AVS_NOTIFY_##msg_type,\
+	.global_msg_type = AVS_GLB_NOTIFICATION,\
+	.msg_direction = AVS_MSG_REPLY,		\
+	.msg_target = AVS_FW_GEN_MSG,		\
+}
+
+#define avs_msg_is_reply(hdr) \
+({ \
+	union avs_reply_msg __msg = AVS_MSG(hdr); \
+	__msg.msg_direction == AVS_MSG_REPLY && \
+	__msg.global_msg_type != AVS_GLB_NOTIFICATION; \
+})
+
+/* Notification types */
+
+struct avs_notify_voice_data {
+	u16 kpd_score;
+	u16 reserved;
+} __packed;
+
+struct avs_notify_res_data {
+	u32 resource_type;
+	u32 resource_id;
+	u32 event_type;
+	u32 reserved;
+	u32 data[6];
+} __packed;
+
+struct avs_notify_mod_data {
+	u32 module_instance_id;
+	u32 event_id;
+	u32 data_size;
+	u32 data[];
+} __packed;
+
+#endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index e0b6c8ffe633..c1db10269c62 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -12,6 +12,11 @@
 /* Intel HD Audio General DSP Registers */
 #define AVS_ADSP_GEN_BASE		0x0
 #define AVS_ADSP_REG_ADSPCS		(AVS_ADSP_GEN_BASE + 0x04)
+#define AVS_ADSP_REG_ADSPIC		(AVS_ADSP_GEN_BASE + 0x08)
+#define AVS_ADSP_REG_ADSPIS		(AVS_ADSP_GEN_BASE + 0x0C)
+
+#define AVS_ADSP_ADSPIC_IPC		BIT(0)
+#define AVS_ADSP_ADSPIS_IPC		BIT(0)
 
 #define AVS_ADSPCS_CRST_MASK(cm)	(cm)
 #define AVS_ADSPCS_CSTALL_MASK(cm)	((cm) << 8)
@@ -19,4 +24,44 @@
 #define AVS_ADSPCS_CPA_MASK(cm)		((cm) << 24)
 #define AVS_MAIN_CORE_MASK		BIT(0)
 
+#define AVS_ADSP_HIPCCTL_BUSY		BIT(0)
+#define AVS_ADSP_HIPCCTL_DONE		BIT(1)
+
+/* SKL Intel HD Audio Inter-Processor Communication Registers */
+#define SKL_ADSP_IPC_BASE		0x40
+#define SKL_ADSP_REG_HIPCT		(SKL_ADSP_IPC_BASE + 0x00)
+#define SKL_ADSP_REG_HIPCTE		(SKL_ADSP_IPC_BASE + 0x04)
+#define SKL_ADSP_REG_HIPCI		(SKL_ADSP_IPC_BASE + 0x08)
+#define SKL_ADSP_REG_HIPCIE		(SKL_ADSP_IPC_BASE + 0x0C)
+#define SKL_ADSP_REG_HIPCCTL		(SKL_ADSP_IPC_BASE + 0x10)
+
+#define SKL_ADSP_HIPCI_BUSY		BIT(31)
+#define SKL_ADSP_HIPCIE_DONE		BIT(30)
+#define SKL_ADSP_HIPCT_BUSY		BIT(31)
+
+/* 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)
+#define AVS_FW_REG_ERROR_CODE(adev)	(AVS_FW_REG_BASE(adev) + 0x4)
+
+#define AVS_FW_REGS_SIZE		PAGE_SIZE
+#define AVS_FW_REGS_WINDOW		0
+/* DSP -> HOST communication window */
+#define AVS_UPLINK_WINDOW		AVS_FW_REGS_WINDOW
+/* HOST -> DSP communication window */
+#define AVS_DOWNLINK_WINDOW		1
+
+/* registry I/O helpers */
+#define avs_sram_offset(adev, window_idx) \
+	((adev)->spec->sram_base_offset + \
+	 (adev)->spec->sram_window_size * (window_idx))
+
+#define avs_sram_addr(adev, window_idx) \
+	((adev)->adsp_ba + avs_sram_offset(adev, window_idx))
+
+#define avs_uplink_addr(adev) \
+	(avs_sram_addr(adev, AVS_UPLINK_WINDOW) + AVS_FW_REGS_SIZE)
+#define avs_downlink_addr(adev) \
+	avs_sram_addr(adev, AVS_DOWNLINK_WINDOW)
+
 #endif /* __SOUND_SOC_INTEL_AVS_REGS_H */
-- 
2.25.1


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

* [PATCH 05/17] ASoC: Intel: avs: Add code loading requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (3 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 04/17] ASoC: Intel: avs: Inter process communication Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-25  1:02   ` Pierre-Louis Bossart
  2022-02-07 12:20 ` [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests Cezary Rojewski
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Before firmware and its modules can be used, they have to be loaded.
Code loading process is complex and is a combination of DMA and IPC
operations. Here, IPC part is being added and accounts for CLDMA and HDA
mechanisms both.

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/messages.c | 65 ++++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h | 22 ++++++++++++
 3 files changed, 88 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/messages.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index e243806dd38a..c0824f30fd3b 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-snd-soc-avs-objs := dsp.o ipc.o
+snd-soc-avs-objs := dsp.o ipc.o messages.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
new file mode 100644
index 000000000000..8dac946dd8dd
--- /dev/null
+++ b/sound/soc/intel/avs/messages.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include "avs.h"
+#include "messages.h"
+
+#define AVS_CL_TIMEOUT_MS	5000
+
+int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.load_multi_mods.mod_cnt = num_mod_ids;
+	request.header = msg.val;
+	request.data = mod_ids;
+	request.size = sizeof(*mod_ids) * num_mod_ids;
+
+	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
+	if (ret)
+		avs_ipc_err(adev, &request, "load multiple modules", ret);
+
+	return ret;
+}
+
+int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.load_multi_mods.mod_cnt = num_mod_ids;
+	request.header = msg.val;
+	request.data = mod_ids;
+	request.size = sizeof(*mod_ids) * num_mod_ids;
+
+	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
+	if (ret)
+		avs_ipc_err(adev, &request, "unload multiple modules", ret);
+
+	return ret;
+}
+
+int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.load_lib.dma_id = dma_id;
+	msg.load_lib.lib_id = lib_id;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
+	if (ret)
+		avs_ipc_err(adev, &request, "load library", ret);
+
+	return ret;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 003e634f5547..b9ec1c64179b 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -24,6 +24,9 @@ enum avs_msg_direction {
 };
 
 enum avs_global_msg_type {
+	AVS_GLB_LOAD_MULTIPLE_MODULES = 15,
+	AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16,
+	AVS_GLB_LOAD_LIBRARY = 24,
 	AVS_GLB_NOTIFICATION = 27,
 };
 
@@ -38,6 +41,16 @@ union avs_global_msg {
 				u32 msg_direction:1;
 				u32 msg_target:1;
 			};
+			/* module loading */
+			struct {
+				u32 mod_cnt:8;
+			} load_multi_mods;
+			/* library loading */
+			struct {
+				u32 dma_id:5;
+				u32 rsvd:11;
+				u32 lib_id:4;
+			} load_lib;
 		};
 		union {
 			u32 val;
@@ -84,6 +97,10 @@ union avs_reply_msg {
 		};
 		union {
 			u32 val;
+			/* module loading */
+			struct {
+				u32 err_mod_id:16;
+			} load_multi_mods;
 		} ext;
 	};
 } __packed;
@@ -167,4 +184,9 @@ struct avs_notify_mod_data {
 	u32 data[];
 } __packed;
 
+/* Code loading messages */
+int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
+int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
+int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (4 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 05/17] ASoC: Intel: avs: Add code loading requests Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-25  1:11   ` Pierre-Louis Bossart
  2022-02-07 12:20 ` [PATCH 07/17] ASoC: Intel: avs: Add module " Cezary Rojewski
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

A 'Pipeline' represents both a container of module instances, and a
scheduling entity. Multiple pipelines can be bound together to create an
audio graph. The Pipeline state machine is entirely controlled by IPCs
(creation, deletion and state changes).

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/messages.c | 76 ++++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h | 56 +++++++++++++++++++++++++
 2 files changed, 132 insertions(+)

diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 8dac946dd8dd..ab13fc7809fe 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -63,3 +63,79 @@ int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
 
 	return ret;
 }
+
+int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+			    u8 instance_id, bool lp, u16 attributes)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.create_ppl.ppl_mem_size = req_size;
+	msg.create_ppl.ppl_priority = priority;
+	msg.create_ppl.instance_id = instance_id;
+	msg.ext.create_ppl.lp = lp;
+	msg.ext.create_ppl.attributes = attributes;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "create pipeline", ret);
+
+	return ret;
+}
+
+int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(DELETE_PIPELINE);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.ppl.instance_id = instance_id;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "delete pipeline", ret);
+
+	return ret;
+}
+
+int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
+			       enum avs_pipeline_state state)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(SET_PIPELINE_STATE);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.set_ppl_state.ppl_id = instance_id;
+	msg.set_ppl_state.state = state;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "set pipeline state", ret);
+
+	return ret;
+}
+
+int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
+			       enum avs_pipeline_state *state)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(GET_PIPELINE_STATE);
+	struct avs_ipc_msg request = {0};
+	struct avs_ipc_msg reply = {0};
+	int ret;
+
+	msg.get_ppl_state.ppl_id = instance_id;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, &reply);
+	if (ret) {
+		avs_ipc_err(adev, &request, "get pipeline state", ret);
+		return ret;
+	}
+
+	*state = reply.rsp.ext.get_ppl_state.state;
+	return ret;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index b9ec1c64179b..67f7e1826e45 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -26,6 +26,10 @@ enum avs_msg_direction {
 enum avs_global_msg_type {
 	AVS_GLB_LOAD_MULTIPLE_MODULES = 15,
 	AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16,
+	AVS_GLB_CREATE_PIPELINE = 17,
+	AVS_GLB_DELETE_PIPELINE = 18,
+	AVS_GLB_SET_PIPELINE_STATE = 19,
+	AVS_GLB_GET_PIPELINE_STATE = 20,
 	AVS_GLB_LOAD_LIBRARY = 24,
 	AVS_GLB_NOTIFICATION = 27,
 };
@@ -45,6 +49,23 @@ union avs_global_msg {
 			struct {
 				u32 mod_cnt:8;
 			} load_multi_mods;
+			/* pipeline management */
+			struct {
+				u32 ppl_mem_size:11;
+				u32 ppl_priority:5;
+				u32 instance_id:8;
+			} create_ppl;
+			struct {
+				u32 rsvd:16;
+				u32 instance_id:8;
+			} ppl; /* generic ppl request */
+			struct {
+				u32 state:16;
+				u32 ppl_id:8;
+			} set_ppl_state;
+			struct {
+				u32 ppl_id:8;
+			} get_ppl_state;
 			/* library loading */
 			struct {
 				u32 dma_id:5;
@@ -54,6 +75,16 @@ union avs_global_msg {
 		};
 		union {
 			u32 val;
+			/* pipeline management */
+			struct {
+				u32 lp:1;
+				u32 rsvd:3;
+				u32 attributes:16;
+			} create_ppl;
+			struct {
+				u32 multi_ppl:1;
+				u32 sync_stop_start:1;
+			} set_ppl_state;
 		} ext;
 	};
 } __packed;
@@ -101,6 +132,10 @@ union avs_reply_msg {
 			struct {
 				u32 err_mod_id:16;
 			} load_multi_mods;
+			/* pipeline management */
+			struct {
+				u32 state:5;
+			} get_ppl_state;
 		} ext;
 	};
 } __packed;
@@ -189,4 +224,25 @@ int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
 int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
 int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id);
 
+/* Pipeline management messages */
+enum avs_pipeline_state {
+	AVS_PPL_STATE_INVALID,
+	AVS_PPL_STATE_UNINITIALIZED,
+	AVS_PPL_STATE_RESET,
+	AVS_PPL_STATE_PAUSED,
+	AVS_PPL_STATE_RUNNING,
+	AVS_PPL_STATE_EOS,
+	AVS_PPL_STATE_ERROR_STOP,
+	AVS_PPL_STATE_SAVED,
+	AVS_PPL_STATE_RESTORED,
+};
+
+int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+			    u8 instance_id, bool lp, u16 attributes);
+int avs_ipc_delete_pipeline(struct avs_dev *adev, u8 instance_id);
+int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
+			       enum avs_pipeline_state state);
+int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
+			       enum avs_pipeline_state *state);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 07/17] ASoC: Intel: avs: Add module management requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (5 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-25  1:27   ` Pierre-Louis Bossart
  2022-02-07 12:20 ` [PATCH 08/17] ASoC: Intel: avs: Add power " Cezary Rojewski
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Firmware modules implement processing algorithms. Their lifecycle,
similarly to pipelines is being controlled by IPCs: initialization,
deletion and (un)binding.

Modules can be configured at runtime - runtime parameters. This is done
with help of LARGE_CONFIG IPCs: getter and setter.

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/ipc.c      |   8 +-
 sound/soc/intel/avs/messages.c | 208 +++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h |  53 +++++++++
 3 files changed, 268 insertions(+), 1 deletion(-)

diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 69178b5d39b1..63f4face5bae 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -20,9 +20,15 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header)
 	union avs_reply_msg msg = AVS_MSG(header);
 
 	ipc->rx.header = header;
-	if (!msg.status)
+	if (!msg.status) {
+		/* update size in case of LARGE_CONFIG_GET */
+		if (msg.msg_target == AVS_MOD_MSG &&
+		    msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET)
+			ipc->rx.size = msg.ext.large_config.data_off_size;
+
 		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
 			      ipc->rx.size);
+	}
 }
 
 static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index ab13fc7809fe..e870d5792a77 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -6,6 +6,7 @@
 //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
 //
 
+#include <linux/slab.h>
 #include "avs.h"
 #include "messages.h"
 
@@ -139,3 +140,210 @@ int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
 	*state = reply.rsp.ext.get_ppl_state.state;
 	return ret;
 }
+
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			  u8 ppl_id, u8 core_id, u8 domain,
+			  void *param, u32 param_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	/* firmware expects size provided in dwords */
+	msg.ext.init_instance.param_block_size =
+			DIV_ROUND_UP(param_size, sizeof(u32));
+	msg.ext.init_instance.ppl_instance_id = ppl_id;
+	msg.ext.init_instance.core_id = core_id;
+	msg.ext.init_instance.proc_domain = domain;
+
+	request.header = msg.val;
+	request.data = param;
+	request.size = param_size;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "init instance", ret);
+
+	return ret;
+}
+
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "delete instance", ret);
+
+	return ret;
+}
+
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		 u16 dst_module_id, u8 dst_instance_id,
+		 u8 dst_queue, u8 src_queue)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.bind_unbind.dst_module_id = dst_module_id;
+	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+	msg.ext.bind_unbind.dst_queue = dst_queue;
+	msg.ext.bind_unbind.src_queue = src_queue;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "bind modules", ret);
+
+	return ret;
+}
+
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		   u16 dst_module_id, u8 dst_instance_id,
+		   u8 dst_queue, u8 src_queue)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.bind_unbind.dst_module_id = dst_module_id;
+	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
+	msg.ext.bind_unbind.dst_queue = dst_queue;
+	msg.ext.bind_unbind.src_queue = src_queue;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "unbind modules", ret);
+
+	return ret;
+}
+
+static int __avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+				      u8 param_id, bool init_block, bool final_block,
+				      u8 *request_data, size_t request_size, size_t off_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_SET);
+	struct avs_ipc_msg request;
+	int ret;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.large_config.data_off_size = off_size;
+	msg.ext.large_config.large_param_id = param_id;
+	msg.ext.large_config.final_block = final_block;
+	msg.ext.large_config.init_block = init_block;
+
+	request.header = msg.val;
+	request.data = request_data;
+	request.size = request_size;
+
+	ret = avs_dsp_send_msg(adev, &request, NULL);
+	if (ret)
+		avs_ipc_err(adev, &request, "large config set", ret);
+
+	return ret;
+}
+
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+			     u8 instance_id, u8 param_id,
+			     u8 *request, size_t request_size)
+{
+	size_t remaining, tx_size;
+	bool final;
+	int ret;
+
+	remaining = request_size;
+	tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+	final = (tx_size == remaining);
+
+	/* Initial request states total payload size. */
+	ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+					 param_id, 1, final, request, tx_size,
+					 request_size);
+	if (ret)
+		return ret;
+
+	remaining -= tx_size;
+
+	/* Loop the rest only when payload exceeds mailbox's size. */
+	while (remaining) {
+		size_t offset;
+
+		offset = request_size - remaining;
+		tx_size = min_t(size_t, AVS_MAILBOX_SIZE, remaining);
+		final = (tx_size == remaining);
+
+		ret = __avs_ipc_set_large_config(adev, module_id, instance_id,
+						 param_id, 0, final,
+						 request + offset, tx_size,
+						 offset);
+		if (ret)
+			return ret;
+
+		remaining -= tx_size;
+	}
+
+	return 0;
+}
+
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			     u8 param_id, u8 *request_data, size_t request_size,
+			     u8 **reply_data, size_t *reply_size)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(LARGE_CONFIG_GET);
+	struct avs_ipc_msg request;
+	struct avs_ipc_msg reply = {0};
+	size_t size;
+	void *buf;
+	int ret;
+
+	reply.data = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+	if (!reply.data)
+		return -ENOMEM;
+
+	msg.module_id = module_id;
+	msg.instance_id = instance_id;
+	msg.ext.large_config.data_off_size = request_size;
+	msg.ext.large_config.large_param_id = param_id;
+	/* final_block is always 0 on request. Updated by fw on reply. */
+	msg.ext.large_config.final_block = 0;
+	msg.ext.large_config.init_block = 1;
+
+	request.header = msg.val;
+	request.data = request_data;
+	request.size = request_size;
+	reply.size = AVS_MAILBOX_SIZE;
+
+	ret = avs_dsp_send_msg(adev, &request, &reply);
+	if (ret) {
+		avs_ipc_err(adev, &request, "large config get", ret);
+		kfree(reply.data);
+		return ret;
+	}
+
+	size = reply.rsp.ext.large_config.data_off_size;
+	buf = krealloc(reply.data, size, GFP_KERNEL);
+	if (!buf) {
+		kfree(reply.data);
+		return -ENOMEM;
+	}
+
+	*reply_data = buf;
+	*reply_size = size;
+
+	return 0;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 67f7e1826e45..1dabd1005327 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -95,6 +95,15 @@ struct avs_tlv {
 	u32 value[];
 } __packed;
 
+enum avs_module_msg_type {
+	AVS_MOD_INIT_INSTANCE = 0,
+	AVS_MOD_LARGE_CONFIG_GET = 3,
+	AVS_MOD_LARGE_CONFIG_SET = 4,
+	AVS_MOD_BIND = 5,
+	AVS_MOD_UNBIND = 6,
+	AVS_MOD_DELETE_INSTANCE = 11,
+};
+
 union avs_module_msg {
 	u64 val;
 	struct {
@@ -110,6 +119,24 @@ union avs_module_msg {
 		};
 		union {
 			u32 val;
+			struct {
+				u32 param_block_size:16;
+				u32 ppl_instance_id:8;
+				u32 core_id:4;
+				u32 proc_domain:1;
+			} init_instance;
+			struct {
+				u32 data_off_size:20;
+				u32 large_param_id:8;
+				u32 final_block:1;
+				u32 init_block:1;
+			} large_config;
+			struct {
+				u32 dst_module_id:16;
+				u32 dst_instance_id:8;
+				u32 dst_queue:3;
+				u32 src_queue:3;
+			} bind_unbind;
 		} ext;
 	};
 } __packed;
@@ -136,6 +163,13 @@ union avs_reply_msg {
 			struct {
 				u32 state:5;
 			} get_ppl_state;
+			/* module management */
+			struct {
+				u32 data_off_size:20;
+				u32 large_param_id:8;
+				u32 final_block:1;
+				u32 init_block:1;
+			} large_config;
 		} ext;
 	};
 } __packed;
@@ -245,4 +279,23 @@ int avs_ipc_set_pipeline_state(struct avs_dev *adev, u8 instance_id,
 int avs_ipc_get_pipeline_state(struct avs_dev *adev, u8 instance_id,
 			       enum avs_pipeline_state *state);
 
+/* Module management messages */
+int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			  u8 ppl_id, u8 core_id, u8 domain,
+			  void *param, u32 param_size);
+int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id,
+			    u8 instance_id);
+int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		 u16 dst_module_id, u8 dst_instance_id,
+		 u8 dst_queue, u8 src_queue);
+int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
+		   u16 dst_module_id, u8 dst_instance_id,
+		   u8 dst_queue, u8 src_queue);
+int avs_ipc_set_large_config(struct avs_dev *adev, u16 module_id,
+			     u8 instance_id, u8 param_id,
+			     u8 *request, size_t request_size);
+int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id,
+			     u8 param_id, u8 *request_data, size_t request_size,
+			     u8 **reply_data, size_t *reply_size);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 08/17] ASoC: Intel: avs: Add power management requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (6 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 07/17] ASoC: Intel: avs: Add module " Cezary Rojewski
@ 2022-02-07 12:20 ` Cezary Rojewski
  2022-02-25  1:37   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 09/17] ASoC: Intel: avs: Add ROM requests Cezary Rojewski
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:20 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Audio DSP supports low power states i.e.: transitions between D0 and D3
and D0-substates in form of D0i3. That process is a combination of core
and IPC operations. Here, Dx and D0ix IPC handlers are added.

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/messages.c | 43 ++++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h | 16 +++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index e870d5792a77..1b589689410f 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -347,3 +347,46 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
 
 	return 0;
 }
+
+int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX);
+	struct avs_ipc_msg request;
+	struct avs_dxstate_info dx;
+	int ret;
+
+	dx.core_mask = core_mask;
+	dx.dx_mask = powerup ? core_mask : 0;
+	request.header = msg.val;
+	request.data = &dx;
+	request.size = sizeof(dx);
+
+	/*
+	 * SET_D0 is sent for non-main cores only while SET_D3 is used to
+	 * suspend for all of them. Both cases prevent any D0I3 transitions.
+	 */
+	ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
+	if (ret)
+		avs_ipc_err(adev, &request, "set dx", ret);
+
+	return ret;
+}
+
+int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
+{
+	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	/* Wake & streaming for < cAVS 2.0 */
+	msg.ext.set_d0ix.wake = enable_pg;
+	msg.ext.set_d0ix.streaming = streaming;
+
+	request.header = msg.val;
+
+	ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
+	if (ret)
+		avs_ipc_err(adev, &request, "set d0ix", ret);
+
+	return ret;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 1dabd1005327..bbdba4631b1f 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -101,6 +101,8 @@ enum avs_module_msg_type {
 	AVS_MOD_LARGE_CONFIG_SET = 4,
 	AVS_MOD_BIND = 5,
 	AVS_MOD_UNBIND = 6,
+	AVS_MOD_SET_DX = 7,
+	AVS_MOD_SET_D0IX = 8,
 	AVS_MOD_DELETE_INSTANCE = 11,
 };
 
@@ -137,6 +139,11 @@ union avs_module_msg {
 				u32 dst_queue:3;
 				u32 src_queue:3;
 			} bind_unbind;
+			struct {
+				/* cAVS < 2.0 */
+				u32 wake:1;
+				u32 streaming:1;
+			} set_d0ix;
 		} ext;
 	};
 } __packed;
@@ -298,4 +305,13 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
 			     u8 param_id, u8 *request_data, size_t request_size,
 			     u8 **reply_data, size_t *reply_size);
 
+/* DSP cores and domains power management messages */
+struct avs_dxstate_info {
+	u32 core_mask;
+	u32 dx_mask;
+} __packed;
+
+int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup);
+int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 09/17] ASoC: Intel: avs: Add ROM requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (7 preceding siblings ...)
  2022-02-07 12:20 ` [PATCH 08/17] ASoC: Intel: avs: Add power " Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  1:42   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 10/17] ASoC: Intel: avs: Add basefw runtime-parameter requests Cezary Rojewski
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

ROM requests are messages initiated by Host to alter firmware early boot
process. They specify whether the next boot should be a fresh start or if
IMR can be used to speed things up.

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/messages.c | 18 ++++++++++++++++++
 sound/soc/intel/avs/messages.h | 14 ++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 1b589689410f..b7a4ba6717b7 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -12,6 +12,24 @@
 
 #define AVS_CL_TIMEOUT_MS	5000
 
+int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge)
+{
+	union avs_global_msg msg = AVS_GLOBAL_REQUEST(ROM_CONTROL);
+	struct avs_ipc_msg request = {0};
+	int ret;
+
+	msg.boot_cfg.rom_ctrl_msg_type = AVS_ROM_SET_BOOT_CONFIG;
+	msg.boot_cfg.dma_id = dma_id;
+	msg.boot_cfg.purge_request = purge;
+	request.header = msg.val;
+
+	ret = avs_dsp_send_rom_msg(adev, &request);
+	if (ret)
+		avs_ipc_err(adev, &request, "set boot config", ret);
+
+	return ret;
+}
+
 int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
 {
 	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES);
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index bbdba4631b1f..580229772395 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -24,6 +24,7 @@ enum avs_msg_direction {
 };
 
 enum avs_global_msg_type {
+	AVS_GLB_ROM_CONTROL = 1,
 	AVS_GLB_LOAD_MULTIPLE_MODULES = 15,
 	AVS_GLB_UNLOAD_MULTIPLE_MODULES = 16,
 	AVS_GLB_CREATE_PIPELINE = 17,
@@ -45,6 +46,12 @@ union avs_global_msg {
 				u32 msg_direction:1;
 				u32 msg_target:1;
 			};
+			/* set boot config */
+			struct {
+				u32 rom_ctrl_msg_type:9;
+				u32 dma_id:5;
+				u32 purge_request:1;
+			} boot_cfg;
 			/* module loading */
 			struct {
 				u32 mod_cnt:8;
@@ -260,6 +267,13 @@ struct avs_notify_mod_data {
 	u32 data[];
 } __packed;
 
+/* ROM messages */
+enum avs_rom_control_msg_type {
+	AVS_ROM_SET_BOOT_CONFIG = 0,
+};
+
+int avs_ipc_set_boot_config(struct avs_dev *adev, u32 dma_id, u32 purge);
+
 /* Code loading messages */
 int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
 int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids);
-- 
2.25.1


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

* [PATCH 10/17] ASoC: Intel: avs: Add basefw runtime-parameter requests
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (8 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 09/17] ASoC: Intel: avs: Add ROM requests Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-07 12:21 ` [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities Cezary Rojewski
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Each module may expose a range of runtime parameters. For basefw,
implement handlers for: FIRMWARE_CONFIG, HARDWARE_CONFIG and
MODULES_INFO. These are used by driver to dynamically allocate resources
in respect to platform details, reducing number of hardcodes and code
duplications that would otherwise be needed to be defined within the
driver code.

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/messages.c | 215 +++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/messages.h | 179 +++++++++++++++++++++++++++
 2 files changed, 394 insertions(+)

diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index b7a4ba6717b7..b9e1bcd749fa 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -408,3 +408,218 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
 
 	return ret;
 }
+
+int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg)
+{
+	struct avs_tlv *tlv;
+	size_t payload_size;
+	size_t offset = 0;
+	u8 *payload;
+	int ret;
+
+	ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+				       AVS_BASEFW_FIRMWARE_CONFIG, NULL, 0,
+				       &payload, &payload_size);
+	if (ret)
+		return ret;
+
+	while (offset < payload_size) {
+		tlv = (struct avs_tlv *)(payload + offset);
+
+		switch (tlv->type) {
+		case AVS_FW_CFG_FW_VERSION:
+			memcpy(&cfg->fw_version, tlv->value,
+				sizeof(cfg->fw_version));
+			break;
+
+		case AVS_FW_CFG_MEMORY_RECLAIMED:
+			cfg->memory_reclaimed = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ:
+			cfg->slow_clock_freq_hz = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_FAST_CLOCK_FREQ_HZ:
+			cfg->fast_clock_freq_hz = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_ALH_SUPPORT_LEVEL:
+			cfg->alh_support = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_IPC_DL_MAILBOX_BYTES:
+			cfg->ipc_dl_mailbox_bytes = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_IPC_UL_MAILBOX_BYTES:
+			cfg->ipc_ul_mailbox_bytes = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_TRACE_LOG_BYTES:
+			cfg->trace_log_bytes = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_PPL_COUNT:
+			cfg->max_ppl_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_ASTATE_COUNT:
+			cfg->max_astate_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_MODULE_PIN_COUNT:
+			cfg->max_module_pin_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MODULES_COUNT:
+			cfg->modules_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_MOD_INST_COUNT:
+			cfg->max_mod_inst_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT:
+			cfg->max_ll_tasks_per_pri_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_LL_PRI_COUNT:
+			cfg->ll_pri_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_DP_TASKS_COUNT:
+			cfg->max_dp_tasks_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_MAX_LIBS_COUNT:
+			cfg->max_libs_count = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_XTAL_FREQ_HZ:
+			cfg->xtal_freq_hz = *tlv->value;
+			break;
+
+		case AVS_FW_CFG_POWER_GATING_POLICY:
+			cfg->power_gating_policy = *tlv->value;
+			break;
+
+		/* Known but not useful to us. */
+		case AVS_FW_CFG_DMA_BUFFER_CONFIG:
+		case AVS_FW_CFG_SCHEDULER_CONFIG:
+		case AVS_FW_CFG_CLOCKS_CONFIG:
+			break;
+
+		default:
+			dev_info(adev->dev, "Unrecognized fw param: %d\n",
+				 tlv->type);
+			break;
+		}
+
+		offset += sizeof(*tlv) + tlv->length;
+	}
+
+	kfree(payload);
+	return ret;
+}
+
+int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg)
+{
+	struct avs_tlv *tlv;
+	size_t payload_size;
+	size_t size, offset = 0;
+	u8 *payload;
+	int ret;
+
+	ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+				       AVS_BASEFW_HARDWARE_CONFIG, NULL, 0,
+				       &payload, &payload_size);
+	if (ret)
+		return ret;
+
+	while (offset < payload_size) {
+		tlv = (struct avs_tlv *)(payload + offset);
+
+		switch (tlv->type) {
+		case AVS_HW_CFG_AVS_VER:
+			cfg->avs_version = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_DSP_CORES:
+			cfg->dsp_cores = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_MEM_PAGE_BYTES:
+			cfg->mem_page_bytes = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES:
+			cfg->total_phys_mem_pages = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_I2S_CAPS:
+			cfg->i2s_caps.i2s_version = tlv->value[0];
+			size = tlv->value[1];
+			cfg->i2s_caps.ctrl_count = size;
+			if (!size)
+				break;
+
+			/* Multiply to get entire array size. */
+			size *= sizeof(*cfg->i2s_caps.ctrl_base_addr);
+			cfg->i2s_caps.ctrl_base_addr = devm_kmemdup(adev->dev,
+								    &tlv->value[2],
+								    size, GFP_KERNEL);
+			if (!cfg->i2s_caps.ctrl_base_addr) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+			break;
+
+		case AVS_HW_CFG_GATEWAY_COUNT:
+			cfg->gateway_count = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_HP_EBB_COUNT:
+			cfg->hp_ebb_count = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_LP_EBB_COUNT:
+			cfg->lp_ebb_count = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_EBB_SIZE_BYTES:
+			cfg->ebb_size_bytes = *tlv->value;
+			break;
+
+		case AVS_HW_CFG_GPDMA_CAPS:
+			break;
+
+		default:
+			dev_info(adev->dev, "Unrecognized hw config: %d\n",
+				 tlv->type);
+			break;
+		}
+
+		offset += sizeof(*tlv) + tlv->length;
+	}
+
+exit:
+	kfree(payload);
+	return ret;
+}
+
+int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
+{
+	size_t payload_size;
+	u8 *payload;
+	int ret;
+
+	ret = avs_ipc_get_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+				       AVS_BASEFW_MODULES_INFO, NULL, 0,
+				       &payload, &payload_size);
+	if (ret)
+		return ret;
+
+	*info = (struct avs_mods_info *)payload;
+	return 0;
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 580229772395..44e8efa980f3 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -328,4 +328,183 @@ struct avs_dxstate_info {
 int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup);
 int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
 
+/* Base-firmware runtime parameters */
+
+#define AVS_BASEFW_MOD_ID	0
+#define AVS_BASEFW_INST_ID	0
+
+enum avs_basefw_runtime_param {
+	AVS_BASEFW_FIRMWARE_CONFIG = 7,
+	AVS_BASEFW_HARDWARE_CONFIG = 8,
+	AVS_BASEFW_MODULES_INFO = 9,
+	AVS_BASEFW_LIBRARIES_INFO = 16,
+};
+
+struct avs_fw_version {
+	u16 major;
+	u16 minor;
+	u16 hotfix;
+	u16 build;
+};
+
+enum avs_fw_cfg_params {
+	AVS_FW_CFG_FW_VERSION = 0,
+	AVS_FW_CFG_MEMORY_RECLAIMED,
+	AVS_FW_CFG_SLOW_CLOCK_FREQ_HZ,
+	AVS_FW_CFG_FAST_CLOCK_FREQ_HZ,
+	AVS_FW_CFG_DMA_BUFFER_CONFIG,
+	AVS_FW_CFG_ALH_SUPPORT_LEVEL,
+	AVS_FW_CFG_IPC_DL_MAILBOX_BYTES,
+	AVS_FW_CFG_IPC_UL_MAILBOX_BYTES,
+	AVS_FW_CFG_TRACE_LOG_BYTES,
+	AVS_FW_CFG_MAX_PPL_COUNT,
+	AVS_FW_CFG_MAX_ASTATE_COUNT,
+	AVS_FW_CFG_MAX_MODULE_PIN_COUNT,
+	AVS_FW_CFG_MODULES_COUNT,
+	AVS_FW_CFG_MAX_MOD_INST_COUNT,
+	AVS_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT,
+	AVS_FW_CFG_LL_PRI_COUNT,
+	AVS_FW_CFG_MAX_DP_TASKS_COUNT,
+	AVS_FW_CFG_MAX_LIBS_COUNT,
+	AVS_FW_CFG_SCHEDULER_CONFIG,
+	AVS_FW_CFG_XTAL_FREQ_HZ,
+	AVS_FW_CFG_CLOCKS_CONFIG,
+	AVS_FW_CFG_RESERVED,
+	AVS_FW_CFG_POWER_GATING_POLICY,
+	AVS_FW_CFG_ASSERT_MODE,
+};
+
+struct avs_fw_cfg {
+	struct avs_fw_version fw_version;
+	u32 memory_reclaimed;
+	u32 slow_clock_freq_hz;
+	u32 fast_clock_freq_hz;
+	u32 alh_support;
+	u32 ipc_dl_mailbox_bytes;
+	u32 ipc_ul_mailbox_bytes;
+	u32 trace_log_bytes;
+	u32 max_ppl_count;
+	u32 max_astate_count;
+	u32 max_module_pin_count;
+	u32 modules_count;
+	u32 max_mod_inst_count;
+	u32 max_ll_tasks_per_pri_count;
+	u32 ll_pri_count;
+	u32 max_dp_tasks_count;
+	u32 max_libs_count;
+	u32 xtal_freq_hz;
+	u32 power_gating_policy;
+};
+
+int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg);
+
+enum avs_hw_cfg_params {
+	AVS_HW_CFG_AVS_VER,
+	AVS_HW_CFG_DSP_CORES,
+	AVS_HW_CFG_MEM_PAGE_BYTES,
+	AVS_HW_CFG_TOTAL_PHYS_MEM_PAGES,
+	AVS_HW_CFG_I2S_CAPS,
+	AVS_HW_CFG_GPDMA_CAPS,
+	AVS_HW_CFG_GATEWAY_COUNT,
+	AVS_HW_CFG_HP_EBB_COUNT,
+	AVS_HW_CFG_LP_EBB_COUNT,
+	AVS_HW_CFG_EBB_SIZE_BYTES,
+};
+
+enum avs_iface_version {
+	AVS_AVS_VER_1_5 = 0x10005,
+	AVS_AVS_VER_1_8 = 0x10008,
+};
+
+enum avs_i2s_version {
+	AVS_I2S_VER_15_SKYLAKE   = 0x00000,
+	AVS_I2S_VER_15_BROXTON   = 0x10000,
+	AVS_I2S_VER_15_BROXTON_P = 0x20000,
+	AVS_I2S_VER_18_KBL_CNL   = 0x30000,
+};
+
+struct avs_i2s_caps {
+	u32 i2s_version;
+	u32 ctrl_count;
+	u32 *ctrl_base_addr;
+};
+
+struct avs_hw_cfg {
+	u32 avs_version;
+	u32 dsp_cores;
+	u32 mem_page_bytes;
+	u32 total_phys_mem_pages;
+	struct avs_i2s_caps i2s_caps;
+	u32 gateway_count;
+	u32 hp_ebb_count;
+	u32 lp_ebb_count;
+	u32 ebb_size_bytes;
+};
+
+int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg);
+
+#define AVS_MODULE_LOAD_TYPE_BUILTIN	0
+#define AVS_MODULE_LOAD_TYPE_LOADABLE	1
+#define AVS_MODULE_STATE_LOADED		BIT(0)
+
+struct avs_module_type {
+	u32 load_type:4;
+	u32 auto_start:1;
+	u32 domain_ll:1;
+	u32 domain_dp:1;
+	u32 lib_code:1;
+	u32 rsvd:24;
+} __packed;
+
+union avs_segment_flags {
+	u32 ul;
+	struct {
+		u32 contents:1;
+		u32 alloc:1;
+		u32 load:1;
+		u32 readonly:1;
+		u32 code:1;
+		u32 data:1;
+		u32 rsvd_1:2;
+		u32 type:4;
+		u32 rsvd_2:4;
+		u32 length:16;
+	};
+} __packed;
+
+struct avs_segment_desc {
+	union avs_segment_flags flags;
+	u32 v_base_addr;
+	u32 file_offset;
+} __packed;
+
+struct avs_module_entry {
+	u16 module_id;
+	u16 state_flags;
+	u8 name[8];
+	guid_t uuid;
+	struct avs_module_type type;
+	u8 hash[32];
+	u32 entry_point;
+	u16 cfg_offset;
+	u16 cfg_count;
+	u32 affinity_mask;
+	u16 instance_max_count;
+	u16 instance_bss_size;
+	struct avs_segment_desc segments[3];
+} __packed;
+
+struct avs_mods_info {
+	u32 count;
+	struct avs_module_entry entries[];
+} __packed;
+
+static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry)
+{
+	return mentry->type.load_type == AVS_MODULE_LOAD_TYPE_BUILTIN ||
+	       mentry->state_flags & AVS_MODULE_STATE_LOADED;
+}
+
+int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (9 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 10/17] ASoC: Intel: avs: Add basefw runtime-parameter requests Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  1:53   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 12/17] ASoC: Intel: avs: Declare module configuration types Cezary Rojewski
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

With basefw runtime parameter handlers added, implement utility
functions to ease pipeline and module allocation. IDA is enlisted to
help with that. Also, as firmware is modular and multiple binaries can
be loaded throughout the lifetime of a driver, custom firmware caching
mechanism is added.

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    |  29 ++++
 sound/soc/intel/avs/utils.c  | 282 +++++++++++++++++++++++++++++++++++
 3 files changed, 312 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/utils.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index c0824f30fd3b..d9f92c5f5407 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-snd-soc-avs-objs := dsp.o ipc.o messages.o
+snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 8620d2f7fee0..d12b19a7299b 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -52,6 +52,13 @@ struct avs_spec {
 	const u32 hipc_ctl_offset;
 };
 
+struct avs_fw_entry {
+	char *name;
+	const struct firmware *fw;
+
+	struct list_head node;
+};
+
 struct avs_dev {
 	struct hda_bus base;
 	struct device *dev;
@@ -60,6 +67,14 @@ struct avs_dev {
 	const struct avs_spec *spec;
 	struct avs_ipc *ipc;
 
+	struct avs_fw_cfg fw_cfg;
+	struct avs_hw_cfg hw_cfg;
+	struct avs_mods_info *mods_info;
+	struct ida **mod_idas;
+	struct mutex modres_mutex;
+	struct ida ppl_ida;
+	struct list_head fw_list;
+
 	struct completion fw_ready;
 };
 
@@ -154,4 +169,18 @@ 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);
 
+/* Firmware resources management */
+
+int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry);
+int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry);
+int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid);
+bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id);
+
+int avs_module_info_init(struct avs_dev *adev, bool purge);
+void avs_module_info_free(struct avs_dev *adev);
+int avs_module_id_alloc(struct avs_dev *adev, u16 module_id);
+void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id);
+int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name);
+void avs_release_firmwares(struct avs_dev *adev);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c
new file mode 100644
index 000000000000..cf8663c32609
--- /dev/null
+++ b/sound/soc/intel/avs/utils.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int avs_module_entry_index(struct avs_dev *adev, const guid_t *uuid)
+{
+	int i;
+
+	for (i = 0; i < adev->mods_info->count; i++) {
+		struct avs_module_entry *module;
+
+		module = &adev->mods_info->entries[i];
+		if (guid_equal(&module->uuid, uuid))
+			return i;
+	}
+
+	return -ENOENT;
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int avs_module_id_entry_index(struct avs_dev *adev, u32 module_id)
+{
+	int i;
+
+	for (i = 0; i < adev->mods_info->count; i++) {
+		struct avs_module_entry *module;
+
+		module = &adev->mods_info->entries[i];
+		if (module->module_id == module_id)
+			return i;
+	}
+
+	return -ENOENT;
+}
+
+int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry)
+{
+	int idx;
+
+	mutex_lock(&adev->modres_mutex);
+
+	idx = avs_module_entry_index(adev, uuid);
+	if (idx >= 0)
+		memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
+
+	mutex_unlock(&adev->modres_mutex);
+	return (idx < 0) ? idx : 0;
+}
+
+int avs_get_module_id_entry(struct avs_dev *adev, u32 module_id, struct avs_module_entry *entry)
+{
+	int idx;
+
+	mutex_lock(&adev->modres_mutex);
+
+	idx = avs_module_id_entry_index(adev, module_id);
+	if (idx >= 0)
+		memcpy(entry, &adev->mods_info->entries[idx], sizeof(*entry));
+
+	mutex_unlock(&adev->modres_mutex);
+	return (idx < 0) ? idx : 0;
+}
+
+int avs_get_module_id(struct avs_dev *adev, const guid_t *uuid)
+{
+	struct avs_module_entry module;
+	int ret;
+
+	ret = avs_get_module_entry(adev, uuid, &module);
+	return !ret ? module.module_id : -ENOENT;
+}
+
+bool avs_is_module_ida_empty(struct avs_dev *adev, u32 module_id)
+{
+	bool ret = false;
+	int idx;
+
+	mutex_lock(&adev->modres_mutex);
+
+	idx = avs_module_id_entry_index(adev, module_id);
+	if (idx >= 0)
+		ret = ida_is_empty(adev->mod_idas[idx]);
+
+	mutex_unlock(&adev->modres_mutex);
+	return ret;
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static void avs_module_ida_destroy(struct avs_dev *adev)
+{
+	int i = adev->mods_info ? adev->mods_info->count : 0;
+
+	while (i--) {
+		ida_destroy(adev->mod_idas[i]);
+		kfree(adev->mod_idas[i]);
+	}
+	kfree(adev->mod_idas);
+}
+
+/* Caller responsible for holding adev->modres_mutex. */
+static int
+avs_module_ida_alloc(struct avs_dev *adev, struct avs_mods_info *newinfo, bool purge)
+{
+	struct avs_mods_info *oldinfo = adev->mods_info;
+	struct ida **ida_ptrs;
+	u32 tocopy_count = 0;
+	int i;
+
+	if (!purge && oldinfo) {
+		if (oldinfo->count >= newinfo->count)
+			dev_warn(adev->dev, "refreshing %d modules info with %d\n",
+				 oldinfo->count, newinfo->count);
+		tocopy_count = oldinfo->count;
+	}
+
+	ida_ptrs = kcalloc(newinfo->count, sizeof(*ida_ptrs), GFP_KERNEL);
+	if (!ida_ptrs)
+		return -ENOMEM;
+
+	if (tocopy_count)
+		memcpy(ida_ptrs, adev->mod_idas, tocopy_count * sizeof(*ida_ptrs));
+
+	for (i = tocopy_count; i < newinfo->count; i++) {
+		ida_ptrs[i] = kzalloc(sizeof(**ida_ptrs), GFP_KERNEL);
+		if (!ida_ptrs[i]) {
+			while (i--)
+				kfree(ida_ptrs[i]);
+
+			kfree(ida_ptrs);
+			return -ENOMEM;
+		}
+
+		ida_init(ida_ptrs[i]);
+	}
+
+	/* If old elements have been reused, don't wipe them. */
+	if (tocopy_count)
+		kfree(adev->mod_idas);
+	else
+		avs_module_ida_destroy(adev);
+
+	adev->mod_idas = ida_ptrs;
+	return 0;
+}
+
+int avs_module_info_init(struct avs_dev *adev, bool purge)
+{
+	struct avs_mods_info *info;
+	int ret;
+
+	ret = avs_ipc_get_modules_info(adev, &info);
+	if (ret)
+		return AVS_IPC_RET(ret);
+
+	mutex_lock(&adev->modres_mutex);
+
+	ret = avs_module_ida_alloc(adev, info, purge);
+	if (ret < 0) {
+		dev_err(adev->dev, "initialize module idas failed: %d\n", ret);
+		goto exit;
+	}
+
+	/* Refresh current information with newly received table. */
+	kfree(adev->mods_info);
+	adev->mods_info = info;
+
+exit:
+	mutex_unlock(&adev->modres_mutex);
+	return ret;
+}
+
+void avs_module_info_free(struct avs_dev *adev)
+{
+	mutex_lock(&adev->modres_mutex);
+
+	avs_module_ida_destroy(adev);
+	kfree(adev->mods_info);
+	adev->mods_info = NULL;
+
+	mutex_unlock(&adev->modres_mutex);
+}
+
+int avs_module_id_alloc(struct avs_dev *adev, u16 module_id)
+{
+	int ret, idx, max_id;
+
+	mutex_lock(&adev->modres_mutex);
+
+	idx = avs_module_id_entry_index(adev, module_id);
+	if (idx == -ENOENT) {
+		WARN(1, "invalid module id: %d", module_id);
+		ret = -EINVAL;
+		goto exit;
+	}
+	max_id = adev->mods_info->entries[idx].instance_max_count - 1;
+	ret = ida_alloc_max(adev->mod_idas[idx], max_id, GFP_KERNEL);
+exit:
+	mutex_unlock(&adev->modres_mutex);
+	return ret;
+}
+
+void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id)
+{
+	int idx;
+
+	mutex_lock(&adev->modres_mutex);
+
+	idx = avs_module_id_entry_index(adev, module_id);
+	if (idx == -ENOENT) {
+		WARN(1, "invalid module id: %d", module_id);
+		goto exit;
+	}
+
+	ida_free(adev->mod_idas[idx], instance_id);
+exit:
+	mutex_unlock(&adev->modres_mutex);
+}
+
+/*
+ * Once driver loads FW it should keep it in memory, so we are not affected
+ * by FW removal from filesystem or even worse by loading different FW at
+ * runtime suspend/resume.
+ */
+int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name)
+{
+	struct avs_fw_entry *entry;
+	int ret;
+
+	/* first check in list if it is not already loaded */
+	list_for_each_entry(entry, &adev->fw_list, node) {
+		if (!strcmp(name, entry->name)) {
+			*fw_p = entry->fw;
+			return 0;
+		}
+	}
+
+	/* FW is not loaded, let's load it now and add to the list */
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->name = kstrdup(name, GFP_KERNEL);
+	if (!entry->name) {
+		kfree(entry);
+		return -ENOMEM;
+	}
+
+	ret = request_firmware(&entry->fw, name, adev->dev);
+	if (ret < 0) {
+		kfree(entry->name);
+		kfree(entry);
+		return ret;
+	}
+
+	*fw_p = entry->fw;
+
+	list_add_tail(&entry->node, &adev->fw_list);
+
+	return 0;
+}
+
+void avs_release_firmwares(struct avs_dev *adev)
+{
+	struct avs_fw_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &adev->fw_list, node) {
+		list_del(&entry->node);
+		release_firmware(entry->fw);
+		kfree(entry->name);
+		kfree(entry);
+	}
+}
-- 
2.25.1


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

* [PATCH 12/17] ASoC: Intel: avs: Declare module configuration types
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (10 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-07 12:21 ` [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management Cezary Rojewski
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Declare structures and constants for all modules being part of basefw
binary. These are used in streaming operations to communicate the needs
of software to firmware side.

While adding module types, append handler for SET_SINK_FORMAT runtime
for COPIER module which allows for configuration of output pin other
than the default one (0).

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/messages.c |  17 +++
 sound/soc/intel/avs/messages.h | 252 +++++++++++++++++++++++++++++++++
 2 files changed, 269 insertions(+)

diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index b9e1bcd749fa..b9d2bd06fe3a 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -623,3 +623,20 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info)
 	*info = (struct avs_mods_info *)payload;
 	return 0;
 }
+
+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,
+				   const struct avs_audio_format *sink_fmt)
+{
+	struct avs_copier_sink_format cpr_fmt;
+
+	cpr_fmt.sink_id = sink_id;
+	/* Firmware expects driver to resend copier's input format. */
+	cpr_fmt.src_fmt = *src_fmt;
+	cpr_fmt.sink_fmt = *sink_fmt;
+
+	return avs_ipc_set_large_config(adev, module_id, instance_id,
+					AVS_COPIER_SET_SINK_FORMAT,
+					(u8 *)&cpr_fmt, sizeof(cpr_fmt));
+}
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 44e8efa980f3..a79aadba161d 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -507,4 +507,256 @@ 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);
 
+/* Module configuration */
+
+#define AVS_MIXIN_MOD_UUID \
+	GUID_INIT(0x39656EB2, 0x3B71, 0x4049, 0x8D, 0x3F, 0xF9, 0x2C, 0xD5, 0xC4, 0x3C, 0x09)
+
+#define AVS_MIXOUT_MOD_UUID \
+	GUID_INIT(0x3C56505A, 0x24D7, 0x418F, 0xBD, 0xDC, 0xC1, 0xF5, 0xA3, 0xAC, 0x2A, 0xE0)
+
+#define AVS_COPIER_MOD_UUID \
+	GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA)
+
+#define AVS_KPBUFF_MOD_UUID \
+	GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6)
+
+#define AVS_MICSEL_MOD_UUID \
+	GUID_INIT(0x32FE92C1, 0x1E17, 0x4FC2, 0x97, 0x58, 0xC7, 0xF3, 0x54, 0x2E, 0x98, 0x0A)
+
+#define AVS_MUX_MOD_UUID \
+	GUID_INIT(0x64CE6E35, 0x857A, 0x4878, 0xAC, 0xE8, 0xE2, 0xA2, 0xF4, 0x2e, 0x30, 0x69)
+
+#define AVS_UPDWMIX_MOD_UUID \
+	GUID_INIT(0x42F8060C, 0x832F, 0x4DBF, 0xB2, 0x47, 0x51, 0xE9, 0x61, 0x99, 0x7b, 0x35)
+
+#define AVS_SRCINTC_MOD_UUID \
+	GUID_INIT(0xE61BB28D, 0x149A, 0x4C1F, 0xB7, 0x09, 0x46, 0x82, 0x3E, 0xF5, 0xF5, 0xAE)
+
+#define AVS_PROBE_MOD_UUID \
+	GUID_INIT(0x7CAD0808, 0xAB10, 0xCD23, 0xEF, 0x45, 0x12, 0xAB, 0x34, 0xCD, 0x56, 0xEF)
+
+#define AVS_AEC_MOD_UUID \
+	GUID_INIT(0x46CB87FB, 0xD2C9, 0x4970, 0x96, 0xD2, 0x6D, 0x7E, 0x61, 0x4B, 0xB6, 0x05)
+
+#define AVS_ASRC_MOD_UUID \
+	GUID_INIT(0x66B4402D, 0xB468, 0x42F2, 0x81, 0xA7, 0xB3, 0x71, 0x21, 0x86, 0x3D, 0xD4)
+
+#define AVS_INTELWOV_MOD_UUID \
+	GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7)
+
+/* channel map */
+enum avs_channel_index {
+	AVS_CHANNEL_LEFT = 0,
+	AVS_CHANNEL_RIGHT = 1,
+	AVS_CHANNEL_CENTER = 2,
+	AVS_CHANNEL_LEFT_SURROUND = 3,
+	AVS_CHANNEL_CENTER_SURROUND = 3,
+	AVS_CHANNEL_RIGHT_SURROUND = 4,
+	AVS_CHANNEL_LFE = 7,
+	AVS_CHANNEL_INVALID = 0xF,
+};
+
+enum avs_channel_config {
+	AVS_CHANNEL_CONFIG_MONO = 0,
+	AVS_CHANNEL_CONFIG_STEREO = 1,
+	AVS_CHANNEL_CONFIG_2_1 = 2,
+	AVS_CHANNEL_CONFIG_3_0 = 3,
+	AVS_CHANNEL_CONFIG_3_1 = 4,
+	AVS_CHANNEL_CONFIG_QUATRO = 5,
+	AVS_CHANNEL_CONFIG_4_0 = 6,
+	AVS_CHANNEL_CONFIG_5_0 = 7,
+	AVS_CHANNEL_CONFIG_5_1 = 8,
+	AVS_CHANNEL_CONFIG_DUAL_MONO = 9,
+	AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_0 = 10,
+	AVS_CHANNEL_CONFIG_I2S_DUAL_STEREO_1 = 11,
+	AVS_CHANNEL_CONFIG_4_CHANNEL = 12,
+	AVS_CHANNEL_CONFIG_INVALID
+};
+
+enum avs_interleaving {
+	AVS_INTERLEAVING_PER_CHANNEL = 0,
+	AVS_INTERLEAVING_PER_SAMPLE = 1,
+};
+
+enum avs_sample_type {
+	AVS_SAMPLE_TYPE_INT_MSB = 0,
+	AVS_SAMPLE_TYPE_INT_LSB = 1,
+	AVS_SAMPLE_TYPE_INT_SIGNED = 2,
+	AVS_SAMPLE_TYPE_INT_UNSIGNED = 3,
+	AVS_SAMPLE_TYPE_FLOAT = 4,
+};
+
+#define AVS_CHANNELS_MAX	8
+#define AVS_ALL_CHANNELS_MASK	UINT_MAX
+
+struct avs_audio_format {
+	u32 sampling_freq;
+	u32 bit_depth;
+	u32 channel_map;
+	u32 channel_config;
+	u32 interleaving;
+	u32 num_channels:8;
+	u32 valid_bit_depth:8;
+	u32 sample_type:8;
+	u32 reserved:8;
+} __packed;
+
+struct avs_modcfg_base {
+	u32 cpc;
+	u32 ibs;
+	u32 obs;
+	u32 is_pages;
+	struct avs_audio_format audio_fmt;
+} __packed;
+
+struct avs_pin_format {
+	u32 pin_index;
+	u32 iobs;
+	struct avs_audio_format audio_fmt;
+} __packed;
+
+struct avs_modcfg_ext {
+	struct avs_modcfg_base base;
+	u16 num_input_pins;
+	u16 num_output_pins;
+	u8 reserved[12];
+	/* input pin formats followed by output ones */
+	struct avs_pin_format pin_fmts[];
+} __packed;
+
+enum avs_dma_type {
+	AVS_DMA_HDA_HOST_OUTPUT = 0,
+	AVS_DMA_HDA_HOST_INPUT = 1,
+	AVS_DMA_HDA_LINK_OUTPUT = 8,
+	AVS_DMA_HDA_LINK_INPUT = 9,
+	AVS_DMA_DMIC_LINK_INPUT = 11,
+	AVS_DMA_I2S_LINK_OUTPUT = 12,
+	AVS_DMA_I2S_LINK_INPUT = 13,
+};
+
+union avs_virtual_index {
+	u8 val;
+	struct {
+		u8 time_slot:4;
+		u8 instance:4;
+	} i2s;
+	struct {
+		u8 queue_id:3;
+		u8 time_slot:2;
+		u8 instance:3;
+	} dmic;
+} __packed;
+
+union avs_connector_node_id {
+	u32 val;
+	struct {
+		u32 vindex:8;
+		u32 dma_type:5;
+		u32 rsvd:19;
+	};
+} __packed;
+
+#define INVALID_PIPELINE_ID	0xFF
+#define INVALID_NODE_ID \
+	((union avs_connector_node_id) { UINT_MAX })
+
+union avs_gtw_attributes {
+	u32 val;
+	struct {
+		u32 lp_buffer_alloc:1;
+		u32 rsvd:31;
+	};
+} __packed;
+
+struct avs_copier_gtw_cfg {
+	union avs_connector_node_id node_id;
+	u32 dma_buffer_size;
+	u32 config_length;
+	struct {
+		union avs_gtw_attributes attrs;
+		u32 blob[];
+	} config;
+} __packed;
+
+struct avs_copier_cfg {
+	struct avs_modcfg_base base;
+	struct avs_audio_format out_fmt;
+	u32 feature_mask;
+	struct avs_copier_gtw_cfg gtw_cfg;
+} __packed;
+
+struct avs_micsel_cfg {
+	struct avs_modcfg_base base;
+	struct avs_audio_format out_fmt;
+} __packed;
+
+struct avs_mux_cfg {
+	struct avs_modcfg_base base;
+	struct avs_audio_format ref_fmt;
+	struct avs_audio_format out_fmt;
+} __packed;
+
+struct avs_updown_mixer_cfg {
+	struct avs_modcfg_base base;
+	u32 out_channel_config;
+	u32 coefficients_select;
+	s32 coefficients[AVS_CHANNELS_MAX];
+	u32 channel_map;
+} __packed;
+
+struct avs_src_cfg {
+	struct avs_modcfg_base base;
+	u32 out_freq;
+} __packed;
+
+struct avs_probe_gtw_cfg {
+	union avs_connector_node_id node_id;
+	u32 dma_buffer_size;
+} __packed;
+
+struct avs_probe_cfg {
+	struct avs_modcfg_base base;
+	struct avs_probe_gtw_cfg gtw_cfg;
+} __packed;
+
+struct avs_aec_cfg {
+	struct avs_modcfg_base base;
+	struct avs_audio_format ref_fmt;
+	struct avs_audio_format out_fmt;
+	u32 cpc_lp_mode;
+} __packed;
+
+struct avs_asrc_cfg {
+	struct avs_modcfg_base base;
+	u32 out_freq;
+	u32 rsvd0:1;
+	u32 mode:1;
+	u32 rsvd2:2;
+	u32 disable_jitter_buffer:1;
+	u32 rsvd3:27;
+} __packed;
+
+struct avs_wov_cfg {
+	struct avs_modcfg_base base;
+	u32 cpc_lp_mode;
+} __packed;
+
+/* Module runtime parameters */
+
+enum avs_copier_runtime_param {
+	AVS_COPIER_SET_SINK_FORMAT = 2,
+};
+
+struct avs_copier_sink_format {
+	u32 sink_id;
+	struct avs_audio_format src_fmt;
+	struct avs_audio_format sink_fmt;
+} __packed;
+
+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,
+				   const struct avs_audio_format *sink_fmt);
+
 #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */
-- 
2.25.1


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

* [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (11 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 12/17] ASoC: Intel: avs: Declare module configuration types Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  2:02   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 14/17] ASoC: Intel: avs: General code loading flow Cezary Rojewski
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Wrap elementary DSP-core operations and resource control into more
complex handlers. This is done to reduce the number of invocations of
wrapped operations throughout the driver as order of operations matters -
most flows involve register manipulation and IPCs combined.

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 |  10 +++
 sound/soc/intel/avs/dsp.c | 170 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 180 insertions(+)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index d12b19a7299b..8779fe9fd8c3 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -74,6 +74,7 @@ struct avs_dev {
 	struct mutex modres_mutex;
 	struct ida ppl_ida;
 	struct list_head fw_list;
+	int *core_refs;
 
 	struct completion fw_ready;
 };
@@ -183,4 +184,13 @@ void avs_module_id_free(struct avs_dev *adev, u16 module_id, u8 instance_id);
 int avs_request_firmware(struct avs_dev *adev, const struct firmware **fw_p, const char *name);
 void avs_release_firmwares(struct avs_dev *adev);
 
+int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
+			u8 core_id, u8 domain, void *param, u32 param_size,
+			u16 *instance_id);
+void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id,
+			   u8 ppl_instance_id, u8 core_id);
+int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+			    bool lp, u16 attributes, u8 *instance_id);
+int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index 258544277bbb..c0291516809d 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -104,4 +104,174 @@ int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
 	return avs_dsp_op(adev, power, core_mask, false);
 }
 
+static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
+{
+	u32 mask;
+	int ret;
+
+	ret = avs_dsp_core_enable(adev, core_mask);
+	if (ret < 0)
+		return ret;
+
+	mask = core_mask & ~AVS_MAIN_CORE_MASK;
+	if (!mask)
+		/*
+		 * without main core, fw is dead anyway
+		 * so setting D0 for it is futile.
+		 */
+		return 0;
+
+	ret = avs_ipc_set_dx(adev, mask, true);
+	return AVS_IPC_RET(ret);
+}
+
+static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
+{
+	int ret;
+
+	ret = avs_ipc_set_dx(adev, core_mask, false);
+	if (ret)
+		return AVS_IPC_RET(ret);
+
+	return avs_dsp_core_disable(adev, core_mask);
+}
+
+static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
+{
+	u32 mask;
+	int ret;
+
+	mask = BIT_MASK(core_id);
+	if (mask == AVS_MAIN_CORE_MASK)
+		/* nothing to do for main core */
+		return 0;
+	if (core_id >= adev->hw_cfg.dsp_cores) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	adev->core_refs[core_id]++;
+	if (adev->core_refs[core_id] == 1) {
+		ret = avs_dsp_enable(adev, mask);
+		if (ret)
+			goto err_enable_dsp;
+	}
+
+	return 0;
+
+err_enable_dsp:
+	adev->core_refs[core_id]--;
+err:
+	dev_err(adev->dev, "get core failed: %d\n", ret);
+	return ret;
+}
+
+static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
+{
+	u32 mask;
+	int ret;
+
+	mask = BIT_MASK(core_id);
+	if (mask == AVS_MAIN_CORE_MASK)
+		/* nothing to do for main core */
+		return 0;
+	if (core_id >= adev->hw_cfg.dsp_cores) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	adev->core_refs[core_id]--;
+	if (!adev->core_refs[core_id]) {
+		ret = avs_dsp_disable(adev, mask);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+err:
+	dev_err(adev->dev, "put core failed: %d\n", ret);
+	return ret;
+}
+
+int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
+			u8 core_id, u8 domain, void *param, u32 param_size,
+			u16 *instance_id)
+{
+	struct avs_module_entry mentry;
+	int ret, id;
+
+	id = avs_module_id_alloc(adev, module_id);
+	if (id < 0)
+		return id;
+
+	ret = avs_get_module_id_entry(adev, module_id, &mentry);
+	if (ret)
+		goto err_mod_entry;
+
+	ret = avs_dsp_get_core(adev, core_id);
+	if (ret)
+		goto err_mod_entry;
+
+	ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
+				    core_id, domain, param, param_size);
+	if (ret) {
+		ret = AVS_IPC_RET(ret);
+		goto err_ipc;
+	}
+
+	*instance_id = id;
+	return 0;
+
+err_ipc:
+	avs_dsp_put_core(adev, core_id);
+err_mod_entry:
+	avs_module_id_free(adev, module_id, id);
+	return ret;
+}
+
+void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id,
+			   u8 ppl_instance_id, u8 core_id)
+{
+	/* Modules not owned by any pipeline need to be freed explicitly. */
+	if (ppl_instance_id == INVALID_PIPELINE_ID)
+		avs_ipc_delete_instance(adev, module_id, instance_id);
+
+	avs_module_id_free(adev, module_id, instance_id);
+
+	avs_dsp_put_core(adev, core_id);
+}
+
+int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
+			    bool lp, u16 attributes, u8 *instance_id)
+{
+	struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;
+	int ret, id;
+
+	id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);
+	if (id < 0)
+		return id;
+
+	ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp,
+				      attributes);
+	if (ret) {
+		ida_free(&adev->ppl_ida, id);
+		return AVS_IPC_RET(ret);
+	}
+
+	*instance_id = id;
+	return 0;
+}
+
+int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
+{
+	int ret;
+
+	ret = avs_ipc_delete_pipeline(adev, instance_id);
+	if (ret)
+		ret = AVS_IPC_RET(ret);
+
+	ida_free(&adev->ppl_ida, instance_id);
+	return ret;
+}
+
 MODULE_LICENSE("GPL v2");
-- 
2.25.1


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

* [PATCH 14/17] ASoC: Intel: avs: General code loading flow
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (12 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  2:15   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer Cezary Rojewski
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Code loading is a complex procedure and requires combined effort of DMA
and IPCs. With IPCs already in place, lay out ground for specific DMA
transfer operations.

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       |  18 +++
 sound/soc/intel/avs/core.c      |  62 +++++++++
 sound/soc/intel/avs/dsp.c       |  26 ++++
 sound/soc/intel/avs/loader.c    | 231 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/registers.h |   6 +
 6 files changed, 344 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/core.c
 create mode 100644 sound/soc/intel/avs/loader.c

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index d9f92c5f5407..d9c793160612 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o
+snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 8779fe9fd8c3..d1a190ada852 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -10,8 +10,11 @@
 #define __SOUND_SOC_INTEL_AVS_H
 
 #include <linux/device.h>
+#include <linux/firmware.h>
 #include <sound/hda_codec.h>
+#include <sound/hda_register.h>
 #include "messages.h"
+#include "registers.h"
 
 struct avs_dev;
 
@@ -22,6 +25,10 @@ struct avs_dsp_ops {
 	irqreturn_t (* const irq_handler)(int, void *);
 	irqreturn_t (* const irq_thread)(int, void *);
 	void (* const int_control)(struct avs_dev *, bool);
+	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);
 };
 
 #define avs_dsp_op(adev, op, ...) \
@@ -35,6 +42,7 @@ struct avs_spec {
 	const char *name;
 
 	const struct avs_dsp_ops *const dops;
+	struct avs_fw_version min_fw_version; /* anything below is rejected */
 
 	const u32 core_init_mask;	/* used during DSP boot */
 	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
@@ -75,6 +83,7 @@ struct avs_dev {
 	struct ida ppl_ida;
 	struct list_head fw_list;
 	int *core_refs;
+	char **lib_names;
 
 	struct completion fw_ready;
 };
@@ -193,4 +202,13 @@ int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
 			    bool lp, u16 attributes, u8 *instance_id);
 int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id);
 
+/* Firmware loading */
+
+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_boot_firmware(struct avs_dev *adev, bool purge);
+int avs_dsp_first_boot_firmware(struct avs_dev *adev);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
new file mode 100644
index 000000000000..117b31ef9cd0
--- /dev/null
+++ b/sound/soc/intel/avs/core.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+// Special thanks to:
+//    Krzysztof Hejmowski <krzysztof.hejmowski@intel.com>
+//    Michal Sienkiewicz <michal.sienkiewicz@intel.com>
+//    Filip Proborszcz
+//
+// for sharing Intel AudioDSP expertise and helping shape the very
+// foundation of this driver
+//
+
+#include <linux/pci.h>
+#include <sound/hdaudio.h>
+#include "avs.h"
+
+static void
+avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value)
+{
+	struct pci_dev *pci = to_pci_dev(bus->dev);
+	u32 data;
+
+	pci_read_config_dword(pci, reg, &data);
+	data &= ~mask;
+	data |= (value & mask);
+	pci_write_config_dword(pci, reg, data);
+}
+
+void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable)
+{
+	u32 value;
+
+	value = enable ? 0 : AZX_PGCTL_LSRMD_MASK;
+	avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL,
+				    AZX_PGCTL_LSRMD_MASK, value);
+}
+
+static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable)
+{
+	u32 value;
+
+	value = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
+	avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL,
+				    AZX_CGCTL_MISCBDCGE_MASK, value);
+}
+
+void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
+{
+	avs_hdac_clock_gating_enable(&adev->base.core, enable);
+}
+
+void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
+{
+	u32 value;
+
+	value = enable ? AZX_VS_EM2_L1SEN : 0;
+	snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value);
+}
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index c0291516809d..30ddde66af33 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -198,6 +198,7 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
 			u16 *instance_id)
 {
 	struct avs_module_entry mentry;
+	bool was_loaded = false;
 	int ret, id;
 
 	id = avs_module_id_alloc(adev, module_id);
@@ -212,6 +213,16 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
 	if (ret)
 		goto err_mod_entry;
 
+	/* Load code into memory if this is the first instance. */
+	if (!id && !avs_module_entry_is_loaded(&mentry)) {
+		ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);
+		if (ret) {
+			dev_err(adev->dev, "load modules failed: %d\n", ret);
+			goto err_mod_entry;
+		}
+		was_loaded = true;
+	}
+
 	ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
 				    core_id, domain, param, param_size);
 	if (ret) {
@@ -223,6 +234,8 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
 	return 0;
 
 err_ipc:
+	if (was_loaded)
+		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
 	avs_dsp_put_core(adev, core_id);
 err_mod_entry:
 	avs_module_id_free(adev, module_id, id);
@@ -232,12 +245,25 @@ int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
 void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id,
 			   u8 ppl_instance_id, u8 core_id)
 {
+	struct avs_module_entry mentry;
+	int ret;
+
 	/* Modules not owned by any pipeline need to be freed explicitly. */
 	if (ppl_instance_id == INVALID_PIPELINE_ID)
 		avs_ipc_delete_instance(adev, module_id, instance_id);
 
 	avs_module_id_free(adev, module_id, instance_id);
 
+	ret = avs_get_module_id_entry(adev, module_id, &mentry);
+	/* Unload occupied memory if this was the last instance. */
+	if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {
+		if (avs_is_module_ida_empty(adev, module_id)) {
+			ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
+			if (ret)
+				dev_err(adev->dev, "unload modules failed: %d\n", ret);
+		}
+	}
+
 	avs_dsp_put_core(adev, core_id);
 }
 
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
new file mode 100644
index 000000000000..e595362323f8
--- /dev/null
+++ b/sound/soc/intel/avs/loader.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "avs.h"
+#include "messages.h"
+#include "registers.h"
+
+#define AVS_FW_INIT_TIMEOUT_MS		3000
+
+#define AVS_ROOT_DIR			"intel/avs"
+#define AVS_BASEFW_FILENAME		"dsp_basefw.bin"
+#define AVS_EXT_MANIFEST_MAGIC		0x31454124
+#define SKL_MANIFEST_MAGIC		0x00000006
+#define SKL_ADSPFW_OFFSET		0x284
+
+static bool debug_ignore_fw_version_check;
+module_param_named(ignore_fw_version, debug_ignore_fw_version_check, bool, 0444);
+MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes (default), 1=no");
+
+#define AVS_LIB_NAME_SIZE	8
+
+struct avs_fw_manifest {
+	u32 id;
+	u32 len;
+	char name[AVS_LIB_NAME_SIZE];
+	u32 preload_page_count;
+	u32 img_flags;
+	u32 feature_mask;
+	struct avs_fw_version version;
+} __packed;
+
+struct avs_fw_ext_manifest {
+	u32 id;
+	u32 len;
+	u16 version_major;
+	u16 version_minor;
+	u32 entries;
+} __packed;
+
+static int avs_fw_ext_manifest_strip(struct firmware *fw)
+{
+	struct avs_fw_ext_manifest *man;
+
+	if (fw->size < sizeof(*man))
+		return -EINVAL;
+
+	man = (struct avs_fw_ext_manifest *)fw->data;
+	if (man->id == AVS_EXT_MANIFEST_MAGIC) {
+		fw->data += man->len;
+		fw->size -= man->len;
+	}
+
+	return 0;
+}
+
+static int avs_fw_manifest_offset(struct firmware *fw)
+{
+	/* Header type found in first DWORD of fw binary. */
+	u32 magic = *(u32 *)fw->data;
+
+	switch (magic) {
+	case SKL_MANIFEST_MAGIC:
+		return SKL_ADSPFW_OFFSET;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw,
+					const struct avs_fw_version *min)
+{
+	struct avs_fw_manifest *man;
+	int offset, ret;
+
+	ret = avs_fw_ext_manifest_strip(fw);
+	if (ret)
+		return ret;
+
+	offset = avs_fw_manifest_offset(fw);
+	if (offset < 0)
+		return offset;
+
+	if (fw->size < offset + sizeof(*man))
+		return -EINVAL;
+	if (!min)
+		return 0;
+
+	man = (struct avs_fw_manifest *)(fw->data + offset);
+	if (man->version.major != min->major ||
+	    man->version.minor != min->minor ||
+	    man->version.hotfix != min->hotfix ||
+	    man->version.build < min->build) {
+		dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n",
+			 man->version.major, man->version.minor,
+			 man->version.hotfix, man->version.build,
+			 min->major, min->minor, min->hotfix, min->build);
+
+		if (!debug_ignore_fw_version_check)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int avs_dsp_load_basefw(struct avs_dev *adev)
+{
+	const struct avs_fw_version *min_req;
+	const struct avs_spec *const spec = adev->spec;
+	const struct firmware *fw;
+	struct firmware stripped_fw;
+	char *filename;
+	int ret;
+
+	filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name,
+			     AVS_BASEFW_FILENAME);
+	if (!filename)
+		return -ENOMEM;
+
+	ret = avs_request_firmware(adev, &fw, filename);
+	kfree(filename);
+	if (ret < 0) {
+		dev_err(adev->dev, "request firmware failed: %d\n", ret);
+		return ret;
+	}
+
+	stripped_fw = *fw;
+	min_req = &adev->spec->min_fw_version;
+
+	ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req);
+	if (ret < 0) {
+		dev_err(adev->dev, "invalid firmware data: %d\n", ret);
+		return ret;
+	}
+
+	ret = avs_dsp_op(adev, load_basefw, &stripped_fw);
+	if (ret < 0) {
+		dev_err(adev->dev, "basefw load failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = wait_for_completion_timeout(&adev->fw_ready,
+					  msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
+	if (!ret) {
+		dev_err(adev->dev, "firmware ready timeout\n");
+		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+{
+	int ret, i;
+
+	/* Full boot, clear cached data except for basefw (slot 0). */
+	for (i = 1; i < adev->fw_cfg.max_libs_count; i++)
+		memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
+
+	avs_hda_clock_gating_enable(adev, false);
+	avs_hda_l1sen_enable(adev, false);
+
+	ret = avs_dsp_load_basefw(adev);
+
+	avs_hda_l1sen_enable(adev, true);
+	avs_hda_clock_gating_enable(adev, true);
+
+	if (ret < 0)
+		return ret;
+
+	/* With all code loaded, refresh module information. */
+	ret = avs_module_info_init(adev, true);
+	if (ret) {
+		dev_err(adev->dev, "init module info failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+{
+	int ret, i;
+
+	ret = avs_dsp_boot_firmware(adev, true);
+	if (ret < 0) {
+		dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg);
+	if (ret) {
+		dev_err(adev->dev, "get hw cfg failed: %d\n", ret);
+		return AVS_IPC_RET(ret);
+	}
+
+	ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg);
+	if (ret) {
+		dev_err(adev->dev, "get fw cfg failed: %d\n", ret);
+		return AVS_IPC_RET(ret);
+	}
+
+	adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores,
+				       sizeof(*adev->core_refs), GFP_KERNEL);
+	adev->lib_names = devm_kcalloc(adev->dev, adev->fw_cfg.max_libs_count,
+				       sizeof(*adev->lib_names), GFP_KERNEL);
+	if (!adev->core_refs || !adev->lib_names)
+		return -ENOMEM;
+
+	for (i = 0; i < adev->fw_cfg.max_libs_count; i++) {
+		adev->lib_names[i] = devm_kzalloc(adev->dev, AVS_LIB_NAME_SIZE,
+						  GFP_KERNEL);
+		if (!adev->lib_names[i])
+			return -ENOMEM;
+	}
+
+	/* basefw always occupies slot 0 */
+	strcpy(&adev->lib_names[0][0], "BASEFW");
+
+	ida_init(&adev->ppl_ida);
+
+	return 0;
+}
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index c1db10269c62..cab5363f62ae 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -9,6 +9,12 @@
 #ifndef __SOUND_SOC_INTEL_AVS_REGS_H
 #define __SOUND_SOC_INTEL_AVS_REGS_H
 
+#define AZX_PCIREG_PGCTL		0x44
+#define AZX_PCIREG_CGCTL		0x48
+#define AZX_PGCTL_LSRMD_MASK		BIT(4)
+#define AZX_CGCTL_MISCBDCGE_MASK	BIT(6)
+#define AZX_VS_EM2_L1SEN		BIT(13)
+
 /* Intel HD Audio General DSP Registers */
 #define AVS_ADSP_GEN_BASE		0x0
 #define AVS_ADSP_REG_ADSPCS		(AVS_ADSP_GEN_BASE + 0x04)
-- 
2.25.1


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

* [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (13 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 14/17] ASoC: Intel: avs: General code loading flow Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  2:18   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA Cezary Rojewski
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

SKL and KBL rely on a dedicated HDAudio DMA stream for code loading and
authentication. The implementation of this specific mechanism for
SKL-based platforms re-uses HDAudio DMA (streaming) functions found in
HDA library to avoid duplication of functionality.

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/cldma.c     | 328 ++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/cldma.h     |  29 +++
 sound/soc/intel/avs/registers.h |   2 +
 4 files changed, 360 insertions(+)
 create mode 100644 sound/soc/intel/avs/cldma.c
 create mode 100644 sound/soc/intel/avs/cldma.h

diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index d9c793160612..f842bfc5e97e 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
 snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o
+snd-soc-avs-objs += cldma.o
 
 obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c
new file mode 100644
index 000000000000..1ec2250e5323
--- /dev/null
+++ b/sound/soc/intel/avs/cldma.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021 Intel Corporation. All rights reserved.
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/pci.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include "cldma.h"
+#include "registers.h"
+
+/* Stream Registers */
+#define AZX_CL_SD_BASE			0x80
+#define AZX_SD_CTL_STRM_MASK		GENMASK(23, 20)
+#define AZX_SD_CTL_STRM(s) \
+	(((s)->stream_tag << 20) & AZX_SD_CTL_STRM_MASK)
+#define AZX_SD_BDLPL_BDLPLBA_MASK	GENMASK(31, 7)
+#define AZX_SD_BDLPL_BDLPLBA(lb)	((lb) & AZX_SD_BDLPL_BDLPLBA_MASK)
+
+/* Software Position Based FIFO Capability Registers */
+#define AZX_CL_SPBFCS			0x20
+#define AZX_REG_CL_SPBFCTL		(AZX_CL_SPBFCS + 0x4)
+#define AZX_REG_CL_SD_SPIB		(AZX_CL_SPBFCS + 0x8)
+
+#define AVS_CL_OP_INTERVAL_US		3
+#define AVS_CL_OP_TIMEOUT_US		300
+#define AVS_CL_IOC_TIMEOUT_MS		300
+#define AVS_CL_STREAM_INDEX		0
+
+struct hda_cldma {
+	struct device *dev;
+	struct hdac_bus *bus;
+	void __iomem *adsp_ba;
+
+	unsigned int buffer_size;
+	unsigned int num_periods;
+	unsigned int stream_tag;
+	void __iomem *sd_addr;
+
+	struct snd_dma_buffer dmab_data;
+	struct snd_dma_buffer dmab_bdl;
+	struct delayed_work memcpy_work;
+	struct completion completion;
+
+	/* runtime */
+	void *position;
+	unsigned int remaining;
+	unsigned int sd_status;
+};
+
+static void cldma_memcpy_work(struct work_struct *work);
+
+struct hda_cldma code_loader = {
+	.stream_tag	= AVS_CL_STREAM_INDEX + 1,
+	.memcpy_work	= __DELAYED_WORK_INITIALIZER(code_loader.memcpy_work,
+						     cldma_memcpy_work, 0),
+	.completion	= COMPLETION_INITIALIZER(code_loader.completion),
+};
+
+void hda_cldma_fill(struct hda_cldma *cl)
+{
+	unsigned int size, offset;
+
+	if (cl->remaining > cl->buffer_size)
+		size = cl->buffer_size;
+	else
+		size = cl->remaining;
+
+	offset = snd_hdac_stream_readl(cl, CL_SD_SPIB);
+	if (offset + size > cl->buffer_size) {
+		unsigned int ss;
+
+		ss = cl->buffer_size - offset;
+		memcpy(cl->dmab_data.area + offset, cl->position, ss);
+		offset = 0;
+		size -= ss;
+		cl->position += ss;
+		cl->remaining -= ss;
+	}
+
+	memcpy(cl->dmab_data.area + offset, cl->position, size);
+	cl->position += size;
+	cl->remaining -= size;
+
+	snd_hdac_stream_writel(cl, CL_SD_SPIB, offset + size);
+}
+
+static void cldma_memcpy_work(struct work_struct *work)
+{
+	struct hda_cldma *cl =
+		container_of(work, struct hda_cldma, memcpy_work.work);
+	int ret;
+
+	ret = hda_cldma_start(cl);
+	if (ret < 0) {
+		dev_err(cl->dev, "cldma set RUN failed: %d\n", ret);
+		return;
+	}
+
+	while (true) {
+		ret = wait_for_completion_timeout(&cl->completion,
+				msecs_to_jiffies(AVS_CL_IOC_TIMEOUT_MS));
+		if (!ret) {
+			dev_err(cl->dev, "cldma IOC timeout\n");
+			break;
+		}
+
+		if (!(cl->sd_status & SD_INT_COMPLETE)) {
+			dev_err(cl->dev, "cldma transfer error, SD status: 0x%08x\n",
+				cl->sd_status);
+			break;
+		}
+
+		if (!cl->remaining)
+			break;
+
+		reinit_completion(&cl->completion);
+		hda_cldma_fill(cl);
+		/* enable CLDMA interrupt */
+		snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
+				      AVS_ADSP_ADSPIC_CLDMA,
+				      AVS_ADSP_ADSPIC_CLDMA);
+	}
+}
+
+void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay)
+{
+	if (!cl->remaining)
+		return;
+
+	reinit_completion(&cl->completion);
+	/* fill buffer with the first chunk before scheduling run */
+	hda_cldma_fill(cl);
+
+	schedule_delayed_work(&cl->memcpy_work, start_delay);
+}
+
+int hda_cldma_start(struct hda_cldma *cl)
+{
+	unsigned int reg;
+
+	/* enable interrupts */
+	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
+			      AVS_ADSP_ADSPIC_CLDMA, AVS_ADSP_ADSPIC_CLDMA);
+	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START,
+				SD_INT_MASK | SD_CTL_DMA_START);
+
+	/* await DMA engine start */
+	return snd_hdac_stream_readb_poll(cl, SD_CTL, reg,
+					  (reg & SD_CTL_DMA_START),
+					  AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+}
+
+int hda_cldma_stop(struct hda_cldma *cl)
+{
+	unsigned int reg;
+	int ret;
+
+	/* disable interrupts */
+	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
+			      AVS_ADSP_ADSPIC_CLDMA, 0);
+	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);
+
+	/* await DMA engine stop */
+	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg,
+					 !(reg & SD_CTL_DMA_START),
+					  AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+	cancel_delayed_work_sync(&cl->memcpy_work);
+
+	return ret;
+}
+
+int hda_cldma_reset(struct hda_cldma *cl)
+{
+	unsigned int reg;
+	int ret;
+
+	ret = hda_cldma_stop(cl);
+	if (ret < 0) {
+		dev_err(cl->dev, "cldma stop failed: %d\n", ret);
+		return ret;
+	}
+
+	snd_hdac_stream_updateb(cl, SD_CTL, 1, 1);
+	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, (reg & 1),
+					 AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(cl->dev, "cldma set SRST failed: %d\n", ret);
+		return ret;
+	}
+
+	snd_hdac_stream_updateb(cl, SD_CTL, 1, 0);
+	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg, !(reg & 1),
+					 AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(cl->dev, "cldma unset SRST failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size)
+{
+	/* setup runtime */
+	cl->position = data;
+	cl->remaining = size;
+}
+
+static void cldma_setup_bdle(struct hda_cldma *cl, u32 bdle_size)
+{
+	struct snd_dma_buffer *dmab = &cl->dmab_data;
+	__le32 *bdl = (__le32 *)cl->dmab_bdl.area;
+	int remaining = cl->buffer_size;
+	int offset = 0;
+
+	cl->num_periods = 0;
+
+	while (remaining > 0) {
+		phys_addr_t addr;
+		int chunk;
+
+		addr = snd_sgbuf_get_addr(dmab, offset);
+		bdl[0] = cpu_to_le32(lower_32_bits(addr));
+		bdl[1] = cpu_to_le32(upper_32_bits(addr));
+		chunk = snd_sgbuf_get_chunk_size(dmab, offset, bdle_size);
+		bdl[2] = cpu_to_le32(chunk);
+
+		remaining -= chunk;
+		/* set IOC only for the last entry */
+		bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01);
+
+		bdl += 4;
+		offset += chunk;
+		cl->num_periods++;
+	}
+}
+
+void hda_cldma_setup(struct hda_cldma *cl)
+{
+	dma_addr_t bdl_addr = cl->dmab_bdl.addr;
+
+	cldma_setup_bdle(cl, cl->buffer_size / 2);
+
+	snd_hdac_stream_writel(cl, SD_BDLPL,
+			       AZX_SD_BDLPL_BDLPLBA(lower_32_bits(bdl_addr)));
+	snd_hdac_stream_writel(cl, SD_BDLPU, upper_32_bits(bdl_addr));
+
+	snd_hdac_stream_writel(cl, SD_CBL, cl->buffer_size);
+	snd_hdac_stream_writeb(cl, SD_LVI, cl->num_periods - 1);
+
+	snd_hdac_stream_updatel(cl, SD_CTL,
+				AZX_SD_CTL_STRM_MASK, AZX_SD_CTL_STRM(cl));
+	/* enable spib */
+	snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);
+}
+
+static irqreturn_t cldma_irq_handler(int irq, void *dev_id)
+{
+	struct hda_cldma *cl = dev_id;
+	u32 adspis;
+
+	adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS);
+	if (adspis == UINT_MAX)
+		return IRQ_NONE;
+	if (!(adspis & AVS_ADSP_ADSPIS_CLDMA))
+		return IRQ_NONE;
+
+	cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
+	dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);
+
+	/* disable CLDMA interrupt */
+	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
+			      AVS_ADSP_ADSPIC_CLDMA, 0);
+
+	complete(&cl->completion);
+
+	return IRQ_HANDLED;
+}
+
+int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus,
+		    void __iomem *adsp_ba, unsigned int buffer_size)
+{
+	struct pci_dev *pci = to_pci_dev(bus->dev);
+	int ret;
+
+	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
+				  buffer_size, &cl->dmab_data);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev,
+				  BDL_SIZE, &cl->dmab_bdl);
+	if (ret < 0)
+		goto alloc_err;
+
+	cl->dev = bus->dev;
+	cl->bus = bus;
+	cl->adsp_ba = adsp_ba;
+	cl->buffer_size = buffer_size;
+	cl->sd_addr = adsp_ba + AZX_CL_SD_BASE;
+
+	ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA");
+	if (ret < 0) {
+		dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret);
+		goto req_err;
+	}
+
+	return 0;
+
+req_err:
+	snd_dma_free_pages(&cl->dmab_bdl);
+alloc_err:
+	snd_dma_free_pages(&cl->dmab_data);
+
+	return ret;
+}
+
+void hda_cldma_free(struct hda_cldma *cl)
+{
+	struct pci_dev *pci = to_pci_dev(cl->dev);
+
+	pci_free_irq(pci, 0, cl);
+	snd_dma_free_pages(&cl->dmab_data);
+	snd_dma_free_pages(&cl->dmab_bdl);
+}
diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h
new file mode 100644
index 000000000000..d99a57061a43
--- /dev/null
+++ b/sound/soc/intel/avs/cldma.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021 Intel Corporation. All rights reserved.
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_CLDMA_H
+#define __SOUND_SOC_INTEL_AVS_CLDMA_H
+
+#define AVS_CL_DEFAULT_BUFFER_SIZE	(32 * PAGE_SIZE)
+
+struct hda_cldma;
+extern struct hda_cldma code_loader;
+
+void hda_cldma_fill(struct hda_cldma *cl);
+void hda_cldma_transfer(struct hda_cldma *cl, unsigned long start_delay);
+
+int hda_cldma_start(struct hda_cldma *cl);
+int hda_cldma_stop(struct hda_cldma *cl);
+int hda_cldma_reset(struct hda_cldma *cl);
+
+void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size);
+void hda_cldma_setup(struct hda_cldma *cl);
+int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus,
+		    void __iomem *adsp_ba, unsigned int buffer_size);
+void hda_cldma_free(struct hda_cldma *cl);
+
+#endif
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index cab5363f62ae..c1ac179b3093 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -22,7 +22,9 @@
 #define AVS_ADSP_REG_ADSPIS		(AVS_ADSP_GEN_BASE + 0x0C)
 
 #define AVS_ADSP_ADSPIC_IPC		BIT(0)
+#define AVS_ADSP_ADSPIC_CLDMA		BIT(1)
 #define AVS_ADSP_ADSPIS_IPC		BIT(0)
+#define AVS_ADSP_ADSPIS_CLDMA		BIT(1)
 
 #define AVS_ADSPCS_CRST_MASK(cm)	(cm)
 #define AVS_ADSPCS_CSTALL_MASK(cm)	((cm) << 8)
-- 
2.25.1


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

* [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (14 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-25  2:21   ` Pierre-Louis Bossart
  2022-02-07 12:21 ` [PATCH 17/17] ASoC: Intel: avs: Code loading over HDA Cezary Rojewski
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

With CLDMA transfer implemented, make use of it to shape firmware,
library and module loading routines for SKL and KBL platforms.

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    |   7 ++
 sound/soc/intel/avs/loader.c | 155 +++++++++++++++++++++++++++++++++++
 2 files changed, 162 insertions(+)

diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index d1a190ada852..657b2894e770 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -34,6 +34,8 @@ struct avs_dsp_ops {
 #define avs_dsp_op(adev, op, ...) \
 	((adev)->spec->dops->op(adev, ## __VA_ARGS__))
 
+#define AVS_PLATATTR_CLDMA		BIT_ULL(0)
+
 #define avs_platattr_test(adev, attr) \
 	((adev)->spec->attributes & AVS_PLATATTR_##attr)
 
@@ -211,4 +213,9 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable);
 int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge);
 int avs_dsp_first_boot_firmware(struct avs_dev *adev);
 
+int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw);
+int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
+int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
+			       struct avs_module_entry *mods, u32 num_mods);
+
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index e595362323f8..1a33b5293c53 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -9,12 +9,24 @@
 #include <linux/firmware.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <sound/hdaudio_ext.h>
 #include "avs.h"
+#include "cldma.h"
 #include "messages.h"
 #include "registers.h"
 
+#define AVS_ROM_STS_MASK		0xFF
+#define AVS_ROM_INIT_DONE		0x1
+#define SKL_ROM_BASEFW_ENTERED		0xF
+#define AVS_ROM_INIT_POLLING_US		5
+#define AVS_ROM_INIT_TIMEOUT_US		300000
+
+#define AVS_FW_INIT_POLLING_US		500
+#define AVS_FW_INIT_TIMEOUT_US		3000000
 #define AVS_FW_INIT_TIMEOUT_MS		3000
 
+#define AVS_CLDMA_START_DELAY_MS	100
+
 #define AVS_ROOT_DIR			"intel/avs"
 #define AVS_BASEFW_FILENAME		"dsp_basefw.bin"
 #define AVS_EXT_MANIFEST_MAGIC		0x31454124
@@ -110,6 +122,140 @@ static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *f
 	return 0;
 }
 
+int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+	struct hda_cldma *cl = &code_loader;
+	unsigned int reg;
+	int ret;
+
+	ret = avs_dsp_op(adev, power, AVS_MAIN_CORE_MASK, true);
+	if (ret < 0)
+		return ret;
+
+	ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false);
+	if (ret < 0)
+		return ret;
+
+	ret = hda_cldma_reset(cl);
+	if (ret < 0) {
+		dev_err(adev->dev, "cldma reset failed: %d\n", ret);
+		return ret;
+	}
+	hda_cldma_setup(cl);
+
+	ret = avs_dsp_op(adev, stall, AVS_MAIN_CORE_MASK, false);
+	if (ret < 0)
+		return ret;
+
+	reinit_completion(&adev->fw_ready);
+	avs_dsp_op(adev, int_control, true);
+
+	/* await ROM init */
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+			(reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE,
+			AVS_ROM_INIT_POLLING_US, AVS_ROM_INIT_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(adev->dev, "rom init timeout: %d\n", ret);
+		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+		return ret;
+	}
+
+	hda_cldma_set_data(cl, (void *)fw->data, fw->size);
+	/* transfer firmware */
+	hda_cldma_transfer(cl, 0);
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+			(reg & AVS_ROM_STS_MASK) == SKL_ROM_BASEFW_ENTERED,
+			AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US);
+	hda_cldma_stop(cl);
+	if (ret < 0) {
+		dev_err(adev->dev, "transfer fw failed: %d\n", ret);
+		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+		return ret;
+	}
+
+	return 0;
+}
+
+int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
+{
+	struct hda_cldma *cl = &code_loader;
+	int ret;
+
+	hda_cldma_set_data(cl, (void *)lib->data, lib->size);
+	/* transfer modules manifest */
+	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
+	/* DMA id ignored as there is only ever one code-loader DMA */
+	ret = avs_ipc_load_library(adev, 0, id);
+	hda_cldma_stop(cl);
+
+	if (ret) {
+		ret = AVS_IPC_RET(ret);
+		dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
+	}
+
+	return ret;
+}
+
+static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry)
+{
+	struct hda_cldma *cl = &code_loader;
+	const struct firmware *mod;
+	char mod_name[128];
+	int ret;
+
+	snprintf(mod_name, sizeof(mod_name), "%s/%s/dsp_mod_%pUL.bin",
+		 AVS_ROOT_DIR, adev->spec->name, mentry->uuid.b);
+
+	ret = avs_request_firmware(adev, &mod, mod_name);
+	if (ret < 0)
+		return ret;
+
+	hda_cldma_set_data(cl, (void *)mod->data, mod->size);
+	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
+	ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
+	hda_cldma_stop(cl);
+
+	if (ret) {
+		dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id,
+			ret);
+		return AVS_IPC_RET(ret);
+	}
+
+	return 0;
+}
+
+int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
+			       struct avs_module_entry *mods, u32 num_mods)
+{
+	u16 *mod_ids;
+	int ret, i;
+
+	/* Either load to DSP or unload them to free space. */
+	if (load) {
+		for (i = 0; i < num_mods; i++) {
+			ret = avs_cldma_load_module(adev, &mods[i]);
+			if (ret)
+				return ret;
+		}
+
+		return 0;
+	}
+
+	mod_ids = kcalloc(num_mods, sizeof(u16), GFP_KERNEL);
+	if (!mod_ids)
+		return -ENOMEM;
+
+	for (i = 0; i < num_mods; i++)
+		mod_ids[i] = mods[i].module_id;
+
+	ret = avs_ipc_unload_modules(adev, mod_ids, num_mods);
+	kfree(mod_ids);
+	if (ret)
+		return AVS_IPC_RET(ret);
+
+	return 0;
+}
+
 static int avs_dsp_load_basefw(struct avs_dev *adev)
 {
 	const struct avs_fw_version *min_req;
@@ -190,6 +336,15 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev)
 {
 	int ret, i;
 
+	if (avs_platattr_test(adev, CLDMA)) {
+		ret = hda_cldma_init(&code_loader, &adev->base.core,
+				      adev->adsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
+		if (ret < 0) {
+			dev_err(adev->dev, "cldma init failed: %d\n", ret);
+			return ret;
+		}
+	}
+
 	ret = avs_dsp_boot_firmware(adev, true);
 	if (ret < 0) {
 		dev_err(adev->dev, "firmware boot failed: %d\n", ret);
-- 
2.25.1


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

* [PATCH 17/17] ASoC: Intel: avs: Code loading over HDA
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (15 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA Cezary Rojewski
@ 2022-02-07 12:21 ` Cezary Rojewski
  2022-02-21 11:51 ` [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
  2022-02-25  2:35 ` Pierre-Louis Bossart
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-07 12:21 UTC (permalink / raw)
  To: alsa-devel
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai,
	pierre-louis.bossart, hdegoede, broonie, amadeuszx.slawinski,
	cujomalainey, lma

Compared to SKL and KBL, younger cAVS platforms are meant to re-use one
of HDAudio streams during boot procedure causing CLDMA to become
obsolete. Once transferred, given stream is returned to pool available
for audio streaming.

Module loading handler is dummy as library and module code became
inseparable in later firmware generations.

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    |   5 +
 sound/soc/intel/avs/loader.c | 208 +++++++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)

diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 5b4941d88101..96aa702d831d 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS
 	default n
 	select SND_SOC_ACPI
 	select SND_HDA_EXT_CORE
+	select SND_HDA_DSP_LOADER
 	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 657b2894e770..20987c7744a3 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -35,6 +35,7 @@ struct avs_dsp_ops {
 	((adev)->spec->dops->op(adev, ## __VA_ARGS__))
 
 #define AVS_PLATATTR_CLDMA		BIT_ULL(0)
+#define AVS_PLATATTR_IMR		BIT_ULL(1)
 
 #define avs_platattr_test(adev, attr) \
 	((adev)->spec->attributes & AVS_PLATATTR_##attr)
@@ -217,5 +218,9 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw);
 int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
 int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
 			       struct avs_module_entry *mods, u32 num_mods);
+int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw);
+int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
+int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
+			     struct avs_module_entry *mods, u32 num_mods);
 
 #endif /* __SOUND_SOC_INTEL_AVS_H */
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index 1a33b5293c53..52c53735dc71 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -9,6 +9,7 @@
 #include <linux/firmware.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <sound/hdaudio.h>
 #include <sound/hdaudio_ext.h>
 #include "avs.h"
 #include "cldma.h"
@@ -18,8 +19,10 @@
 #define AVS_ROM_STS_MASK		0xFF
 #define AVS_ROM_INIT_DONE		0x1
 #define SKL_ROM_BASEFW_ENTERED		0xF
+#define APL_ROM_FW_ENTERED		0x5
 #define AVS_ROM_INIT_POLLING_US		5
 #define AVS_ROM_INIT_TIMEOUT_US		300000
+#define AVS_ROM_INIT_RETRIES		3
 
 #define AVS_FW_INIT_POLLING_US		500
 #define AVS_FW_INIT_TIMEOUT_US		3000000
@@ -256,6 +259,202 @@ int avs_cldma_transfer_modules(struct avs_dev *adev, bool load,
 	return 0;
 }
 
+static int
+avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge)
+{
+	const struct avs_spec *const spec = adev->spec;
+	unsigned int corex_mask, reg;
+	int ret;
+
+	corex_mask = spec->core_init_mask & ~AVS_MAIN_CORE_MASK;
+
+	ret = avs_dsp_op(adev, power, spec->core_init_mask, true);
+	if (ret < 0)
+		goto err;
+
+	ret = avs_dsp_op(adev, reset, AVS_MAIN_CORE_MASK, false);
+	if (ret < 0)
+		goto err;
+
+	reinit_completion(&adev->fw_ready);
+	avs_dsp_op(adev, int_control, true);
+
+	/* set boot config */
+	ret = avs_ipc_set_boot_config(adev, dma_id, purge);
+	if (ret) {
+		ret = AVS_IPC_RET(ret);
+		goto err;
+	}
+
+	/* await ROM init */
+	ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg,
+				       (reg & 0xF) == AVS_ROM_INIT_DONE ||
+				       (reg & 0xF) == APL_ROM_FW_ENTERED,
+				       AVS_ROM_INIT_POLLING_US, AVS_ROM_INIT_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(adev->dev, "rom init timeout: %d\n", ret);
+		goto err;
+	}
+
+	/* power down non-main cores */
+	if (corex_mask)
+		avs_dsp_op(adev, power, corex_mask, false);
+
+	return 0;
+
+err:
+	avs_dsp_core_disable(adev, spec->core_init_mask);
+	return ret;
+}
+
+static int avs_imr_load_basefw(struct avs_dev *adev)
+{
+	int ret;
+
+	/* DMA id ignored when flashing from IMR as no transfer occurs. */
+	ret = avs_hda_init_rom(adev, 0, false);
+	if (ret < 0) {
+		dev_err(adev->dev, "rom init failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = wait_for_completion_timeout(&adev->fw_ready,
+				msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
+	if (!ret) {
+		dev_err(adev->dev, "firmware ready timeout\n");
+		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw)
+{
+	struct snd_pcm_substream substream = {0};
+	struct snd_dma_buffer dmab;
+	struct hdac_ext_stream *estream;
+	struct hdac_stream *hstream;
+	struct hdac_bus *bus = &adev->base.core;
+	unsigned int sdfmt, reg;
+	int ret, i;
+
+	/* configure hda dma */
+	substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+	estream = snd_hdac_ext_stream_assign(bus, &substream,
+					     HDAC_EXT_STREAM_TYPE_HOST);
+	if (!estream)
+		return -ENODEV;
+	hstream = hdac_stream(estream);
+
+	/* code loading performed with default format */
+	sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+	ret = snd_hdac_dsp_prepare(hstream, sdfmt, fw->size, &dmab);
+	if (ret < 0)
+		goto release_stream;
+
+	/* enable SPIB for hda stream */
+	snd_hdac_ext_stream_spbcap_enable(bus, true, hstream->index);
+	ret = snd_hdac_ext_stream_set_spib(bus, estream, fw->size);
+	if (ret)
+		goto cleanup_resources;
+
+	memcpy(dmab.area, fw->data, fw->size);
+
+	for (i = 0; i < AVS_ROM_INIT_RETRIES; i++) {
+		unsigned int dma_id = hstream->stream_tag - 1;
+
+		ret = avs_hda_init_rom(adev, dma_id, true);
+		if (!ret)
+			break;
+		dev_info(adev->dev, "#%d rom init fail: %d\n", i + 1, ret);
+	}
+	if (ret < 0)
+		goto cleanup_resources;
+
+	/* transfer firmware */
+	snd_hdac_dsp_trigger(hstream, true);
+	ret = snd_hdac_adsp_readl_poll(adev, AVS_FW_REG_STATUS(adev), reg,
+				       (reg & AVS_ROM_STS_MASK) == APL_ROM_FW_ENTERED,
+				       AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US);
+	snd_hdac_dsp_trigger(hstream, false);
+	if (ret < 0) {
+		dev_err(adev->dev, "transfer fw failed: %d\n", ret);
+		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+	}
+
+cleanup_resources:
+	/* disable SPIB for hda stream */
+	snd_hdac_ext_stream_spbcap_enable(bus, false, hstream->index);
+	snd_hdac_ext_stream_set_spib(bus, estream, 0);
+
+	snd_hdac_dsp_cleanup(hstream, &dmab);
+release_stream:
+	snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+	return ret;
+}
+
+int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
+{
+	struct snd_pcm_substream substream = {0};
+	struct snd_dma_buffer dmab;
+	struct hdac_ext_stream *estream;
+	struct hdac_stream *stream;
+	struct hdac_bus *bus = &adev->base.core;
+	unsigned int sdfmt;
+	int ret;
+
+	/* configure hda dma */
+	substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+	estream = snd_hdac_ext_stream_assign(bus, &substream,
+					     HDAC_EXT_STREAM_TYPE_HOST);
+	if (!estream)
+		return -ENODEV;
+	stream = hdac_stream(estream);
+
+	/* code loading performed with default format */
+	sdfmt = snd_hdac_calc_stream_format(48000, 1, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+	ret = snd_hdac_dsp_prepare(stream, sdfmt, lib->size, &dmab);
+	if (ret < 0)
+		goto release_stream;
+
+	/* enable SPIB for hda stream */
+	snd_hdac_ext_stream_spbcap_enable(bus, true, stream->index);
+	snd_hdac_ext_stream_set_spib(bus, estream, lib->size);
+
+	memcpy(dmab.area, lib->data, lib->size);
+
+	/* transfer firmware */
+	snd_hdac_dsp_trigger(stream, true);
+	ret = avs_ipc_load_library(adev, stream->stream_tag - 1, id);
+	snd_hdac_dsp_trigger(stream, false);
+	if (ret) {
+		dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
+		ret = AVS_IPC_RET(ret);
+	}
+
+	/* disable SPIB for hda stream */
+	snd_hdac_ext_stream_spbcap_enable(bus, false, stream->index);
+	snd_hdac_ext_stream_set_spib(bus, estream, 0);
+
+	snd_hdac_dsp_cleanup(stream, &dmab);
+release_stream:
+	snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+	return ret;
+}
+
+int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
+			     struct avs_module_entry *mods, u32 num_mods)
+{
+	/*
+	 * All platforms without CLDMA are equipped with IMR,
+	 * and thus the module transferring is offloaded to DSP.
+	 */
+	return 0;
+}
+
 static int avs_dsp_load_basefw(struct avs_dev *adev)
 {
 	const struct avs_fw_version *min_req;
@@ -307,6 +506,15 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
 {
 	int ret, i;
 
+	/* Forgo full boot if flash from IMR succeeds. */
+	if (!purge && avs_platattr_test(adev, IMR)) {
+		ret = avs_imr_load_basefw(adev);
+		if (!ret)
+			return 0;
+
+		dev_dbg(adev->dev, "firmware flash from imr failed: %d\n", ret);
+	}
+
 	/* Full boot, clear cached data except for basefw (slot 0). */
 	for (i = 1; i < adev->fw_cfg.max_libs_count; i++)
 		memset(adev->lib_names[i], 0, AVS_LIB_NAME_SIZE);
-- 
2.25.1


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

* Re: [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (16 preceding siblings ...)
  2022-02-07 12:21 ` [PATCH 17/17] ASoC: Intel: avs: Code loading over HDA Cezary Rojewski
@ 2022-02-21 11:51 ` Cezary Rojewski
  2022-02-25  2:35 ` Pierre-Louis Bossart
  18 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-21 11:51 UTC (permalink / raw)
  To: alsa-devel, Mark Brown
  Cc: rad, upstream, harshapriya.n, tiwai, pierre-louis.bossart,
	hdegoede, amadeuszx.slawinski, cujomalainey, lma

On 2022-02-07 1:20 PM, Cezary Rojewski wrote:
> A continuation of cleanup work of Intel SST solutions found in
> sound/soc/intel/. With two major chapters released last year catpt [1]
> and removal of haswell solution [2], time has come for Skylake-driver.
> 
> Througout 2019, 2020 and 2021 Skylake-driver has had many fixes applied
> and even attempts of refactors as seen in fundamental overhaul [3], IPC
> flow adjustments [4] and LARGE_CONFIG overhaul [5] series.
> Unfortunately, story repeats itself - problems are found within the core
> of a driver. Painting it with different colors does not change the fact
> that is it still a house of cards. As changes needed to address those
> issues would make Skylake solution incompatible with its previous
> revisions, a decision has been made to provide a new solution instead.
> In time it would deprecate and replace Skylake-driver.
> 
> That solution has been called AVS - from AudioDSP architecture name:
> Audio-Voice-Speech. It is meant to provide support for the exact same
> range of platforms as its predecessor: SKL, KBL, AML and APL.
> 
> Note: this series is dependent upon HDA-series [6] which exposes several
> codec-organization functions allowing for reduced code size on
> avs-driver side.


Hello,

Despite HDA-series being updated to v2 [1], no changes are required on 
the side of this series.


Mark,

Should I resend this one regardless of the above?
Also, is there anything else I can help with or explain further 
regarding code-loading and IPC protocol which this series implements?


Regards,
Czarek


[1]: 
https://lore.kernel.org/alsa-devel/20220214101404.4074026-1-cezary.rojewski@intel.com/T/#t

> Note: this series does not add fully functional driver as its size would
> get out of control. Here, focus is put on adding IPC protocol and code
> loading code.

...

> [1]: https://www.spinics.net/lists/alsa-devel/msg116440.html
> [2]: https://www.spinics.net/lists/alsa-devel/msg116901.html
> [3]: https://www.spinics.net/lists/alsa-devel/msg94199.html
> [4]: https://www.spinics.net/lists/alsa-devel/msg92588.html
> [5]: https://lore.kernel.org/all/20190808181549.12521-1-cezary.rojewski@intel.com/
> [6]: https://lore.kernel.org/alsa-devel/20220207114906.3759800-1-cezary.rojewski@intel.com/T/#t
> [7]: https://lore.kernel.org/all/20211208111301.1817725-1-cezary.rojewski@intel.com/

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

* Re: [PATCH 03/17] ASoC: Intel: Introduce AVS driver
  2022-02-07 12:20 ` [PATCH 03/17] ASoC: Intel: Introduce AVS driver Cezary Rojewski
@ 2022-02-24 23:55   ` Pierre-Louis Bossart
  2022-02-25 16:56     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-24 23:55 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma




> +config SND_SOC_INTEL_AVS
> +	tristate "Intel AVS driver"
> +	depends on PCI && ACPI
> +	depends on COMMON_CLK
> +	depends on SND_SOC_INTEL_SKYLAKE_FAMILY=n
> +	default n

default is already n

> +	select SND_SOC_ACPI
> +	select SND_HDA_EXT_CORE
> +	help
> +	  Enable support for Intel(R) cAVS 1.5 platforms with DSP
> +	  capabilities. This includes Skylake, Kabylake, Amberlake and
> +	  Apollolake. This option is mutually exclusive with SKYLAKE
> +	  driver.

The feedback from the RFC was that this is not desirable if you want
anyone to use this driver. The suggested solution was to use the
intel_dspcfg layer with e.g. dsp_driver=4 for avs. That would allow
distributions to build this solution for early adopters.


> +/* Platform specific descriptor */
> +struct avs_spec {
> +	const char *name;
> +
> +	const struct avs_dsp_ops *const dops;

dsp_ops would be clearer. 'd' could refer to just about anything.

> +	const u32 core_init_mask;	/* used during DSP boot */
> +	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
> +};
> +
> +struct avs_dev {
> +	struct hda_bus base;
> +	struct device *dev;

question: could you directly embed a struct device instead of a pointer,
that would simplify the conversion through dev_get_drvdata below.

Unless this *dev is related to the PCI device, in which case you could
add a comment.

> +
> +	void __iomem *adsp_ba;

I would guess 'ba' is base address? this could be added with comments or
kernel-doc

> +	const struct avs_spec *spec;
> +};
> +
> +/* from hda_bus to avs_dev */
> +#define hda_to_avs(hda) container_of(hda, struct avs_dev, base)
> +/* from hdac_bus to avs_dev */
> +#define hdac_to_avs(hdac) hda_to_avs(to_hda_bus(hdac))
> +/* from device to avs_dev */
> +#define to_avs_dev(dev) \
> +({ \
> +	struct hdac_bus *__bus = dev_get_drvdata(dev); \
> +	hdac_to_avs(__bus); \
> +})
> +
> +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active);

does this mean 'active' affects all bits in the core_mask? that doesn't
seem very intuitive.

> +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset);
> +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
> +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);

it's a bit inconsistent to have enable/disable but a boolean for other
functions?


> +#include <linux/module.h>
> +#include <sound/hdaudio_ext.h>
> +#include "avs.h"
> +#include "registers.h"

consider renaming as avs_registers.h?

> +
> +#define AVS_ADSPCS_INTERVAL_US		500
> +#define AVS_ADSPCS_TIMEOUT_US		10000

these values don't match with anything that was previously used for
Intel platforms, where the values could be different depending on
generations.

bxt-sst.c:#define BXT_BASEFW_TIMEOUT    3000
bxt-sst.c:#define BXT_ROM_INIT_TIMEOUT  70
cnl-sst.c:#define CNL_INIT_TIMEOUT      300
cnl-sst.c:#define CNL_BASEFW_TIMEOUT    3000
skl-sst-cldma.h:#define SKL_WAIT_TIMEOUT                500     /* 500
msec */
skl-sst-dsp.h:#define BXT_INIT_TIMEOUT          300
skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000
skl-sst.c:#define SKL_BASEFW_TIMEOUT    300
skl-sst.c:#define SKL_INIT_TIMEOUT      1000

please add a comment on how they were determined or align on hardware
recommendations.

> +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active)
> +{
> +	u32 value, mask, reg;
> +	int ret;
> +
> +	mask = AVS_ADSPCS_SPA_MASK(core_mask);
> +	value = active ? mask : 0;
> +
> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
> +
> +	mask = AVS_ADSPCS_CPA_MASK(core_mask);
> +	value = active ? mask : 0;
> +
> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
> +				       reg, (reg & mask) == value,
> +				       AVS_ADSPCS_INTERVAL_US,
> +				       AVS_ADSPCS_TIMEOUT_US);
> +	if (ret)
> +		dev_err(adev->dev, "core_mask %d %spower failed: %d\n",
> +			core_mask, active ? "" : "un", ret);

unpower is an odd wording.

> +
> +	return ret;
> +}
> +
> +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
> +{
> +	u32 value, mask, reg;
> +	int ret;
> +
> +	mask = AVS_ADSPCS_CRST_MASK(core_mask);
> +	value = reset ? mask : 0;
> +
> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
> +
> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
> +				       reg, (reg & mask) == value,
> +				       AVS_ADSPCS_INTERVAL_US,
> +				       AVS_ADSPCS_TIMEOUT_US);
> +	if (ret)
> +		dev_err(adev->dev, "core_mask %d %sreset failed: %d\n",
> +			core_mask, reset ? "" : "un", ret);

unreset is even more odd. enter reset or exit reset.

> +
> +	return ret;
> +}
> +
> +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
> +{
> +	u32 value, mask, reg;
> +	int ret;
> +
> +	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
> +	value = stall ? mask : 0;
> +
> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
> +
> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
> +				       reg, (reg & mask) == value,
> +				       AVS_ADSPCS_INTERVAL_US,
> +				       AVS_ADSPCS_TIMEOUT_US);
> +	if (ret)
> +		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
> +			core_mask, stall ? "" : "un", ret);

that was probably a copy/paste of stall/unstall in the two cases
above...this one works, the two above not so much.

> +
> +	return ret;
> +}
> +
> +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
> +{
> +	int ret;
> +
> +	ret = avs_dsp_op(adev, power, core_mask, true);
> +	if (ret)
> +		return ret;
> +
> +	ret = avs_dsp_op(adev, reset, core_mask, false);
> +	if (ret)
> +		return ret;
> +
> +	return avs_dsp_op(adev, stall, core_mask, false);
> +}
> +
> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
> +{
> +	/* Be permissive to allow for full DSP shutdown in disable path. */

that comment isn't very clear, what is permissive here?

> +	avs_dsp_op(adev, stall, core_mask, true);
> +	avs_dsp_op(adev, reset, core_mask, true);
> +
> +	return avs_dsp_op(adev, power, core_mask, false);
> +}
> +
> +MODULE_LICENSE("GPL v2");

"GPL"

> diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
> new file mode 100644
> index 000000000000..e0b6c8ffe633
> --- /dev/null
> +++ b/sound/soc/intel/avs/registers.h

avs_registers.h?

> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright(c) 2021 Intel Corporation. All rights reserved.
> + *
> + * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
> + *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
> + */
> +
> +#ifndef __SOUND_SOC_INTEL_AVS_REGS_H
> +#define __SOUND_SOC_INTEL_AVS_REGS_H
> +
> +/* Intel HD Audio General DSP Registers */
> +#define AVS_ADSP_GEN_BASE		0x0
> +#define AVS_ADSP_REG_ADSPCS		(AVS_ADSP_GEN_BASE + 0x04)
> +
> +#define AVS_ADSPCS_CRST_MASK(cm)	(cm)
> +#define AVS_ADSPCS_CSTALL_MASK(cm)	((cm) << 8)
> +#define AVS_ADSPCS_SPA_MASK(cm)		((cm) << 16)
> +#define AVS_ADSPCS_CPA_MASK(cm)		((cm) << 24)
> +#define AVS_MAIN_CORE_MASK		BIT(0)
> +
> +#endif /* __SOUND_SOC_INTEL_AVS_REGS_H */

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

* Re: [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-07 12:20 ` [PATCH 04/17] ASoC: Intel: avs: Inter process communication Cezary Rojewski
@ 2022-02-25  0:56   ` Pierre-Louis Bossart
  2022-02-25 18:06     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  0:56 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma




> The boot process involving ROM-code requires specific handling with
> 'unstall' operations which are not required post-boot with normal IPC so
> separate set of send-message handlers is added for each of the usecases.

consider splitting this long sentence and use simpler logic. It's quite
unclear how you went from boot to use cases.


> +#include "messages.h"

avs_messages.h?

>  
>  struct avs_dev;
>  
> @@ -18,6 +19,9 @@ struct avs_dsp_ops {
>  	int (* const power)(struct avs_dev *, u32, bool);
>  	int (* const reset)(struct avs_dev *, u32, bool);
>  	int (* const stall)(struct avs_dev *, u32, bool);
> +	irqreturn_t (* const irq_handler)(int, void *);
> +	irqreturn_t (* const irq_thread)(int, void *);
> +	void (* const int_control)(struct avs_dev *, bool);

kernel-doc or comments on what the last op might mean?

>  };
>  
>  #define avs_dsp_op(adev, op, ...) \
> @@ -34,6 +38,18 @@ struct avs_spec {
>  
>  	const u32 core_init_mask;	/* used during DSP boot */
>  	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
> +	const u32 sram_base_offset;
> +	const u32 sram_window_size;
> +
> +	const u32 rom_status;
> +	const u32 hipc_req_offset;
> +	const u32 hipc_req_ext_offset;
> +	const u32 hipc_req_busy_mask;
> +	const u32 hipc_ack_offset;
> +	const u32 hipc_ack_done_mask;
> +	const u32 hipc_rsp_offset;
> +	const u32 hipc_rsp_busy_mask;
> +	const u32 hipc_ctl_offset;

is this really desirable to describe the IPC registers, when we know
there were 3 generations of Intel IPC registers. this is ipc-1.5 only.

>  };
>  
>  struct avs_dev {
> @@ -42,6 +58,9 @@ struct avs_dev {
>  
>  	void __iomem *adsp_ba;
>  	const struct avs_spec *spec;
> +	struct avs_ipc *ipc;
> +
> +	struct completion fw_ready;
>  };
>  
>  /* from hda_bus to avs_dev */
> @@ -61,4 +80,78 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
>  int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
>  int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
>  
> +/* Inter Process Communication */
> +
> +struct avs_ipc_msg {
> +	union {
> +		u64 header;
> +		union avs_global_msg glb;
> +		union avs_reply_msg rsp;
> +	};
> +	void *data;
> +	size_t size;
> +};
> +
> +struct avs_ipc {
> +	struct device *dev;
> +
> +	struct avs_ipc_msg rx;
> +	u32 default_timeout_ms;
> +	bool ready;

ready for what? This should be described or documented.

> +
> +	bool rx_completed;
> +	spinlock_t rx_lock;
> +	struct mutex msg_mutex;

checkpatch would tell you to add a comment for spinlock and mutex. it's
quite unclear what they might describe and if they are related.

> +	struct completion done_completion;
> +	struct completion busy_completion;
> +};
> +
> +#define AVS_EIPC	EREMOTEIO
> +/*
> + * IPC handlers may return positive value (firmware error code) what denotes
> + * successful HOST <-> DSP communication yet failure to process specific request.
> + *
> + * Below macro converts returned value to linux kernel error code.
> + * All IPC callers MUST use it as soon as firmware error code is consumed.
> + */
> +#define AVS_IPC_RET(ret) \
> +	(((ret) <= 0) ? (ret) : -AVS_EIPC)

why not use -EREMOTEIO directly? -AVS_EIPC is not very useful for the
reader.

And why -EREMOTEIO? I see that you used it in catpt but that's a very
surprising code that no one else uses in sound/

> +
> +static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx,
> +			       const char *name, int error)
> +{
> +	/*
> +	 * If IPC channel is blocked e.g.: due to ongoing recovery,
> +	 * -EPERM error code is expected and thus it's not an actual error.
> +	 */
> +	if (error == -EPERM)
> +		dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
> +			tx->glb.primary, tx->glb.ext.val, error);
> +	else
> +		dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
> +			tx->glb.primary, tx->glb.ext.val, error);
> +}

we've used such functions before and the feedback, e.g. from GregKH and
Mark Brown, has consistenly been that this is pushing the use of dev_dbg
too far.


> +#define AVS_IPC_TIMEOUT_MS	300

skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000

that's one order of magniture lower. please add a comment or align.

> +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);
> +
> +	ipc->rx.header = header;
> +	if (!msg.status)
> +		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
> +			      ipc->rx.size);

it wouldn't hurt to describe that the status determines whether
additional information can be read from a mailbox.

> +}
> +
> +static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
> +{
> +	struct avs_notify_mod_data mod_data;
> +	union avs_notify_msg msg = AVS_MSG(header);
> +	size_t data_size = 0;
> +	void *data = NULL;
> +
> +	if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
> +		dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n",
> +			msg.primary);

can this happen?

you should add a comment on what could be sent before the first 'real'
sign of life from the DSP.

it's also unclear why this dev_dbg() when 'unknown notifications' below
are handled as dev_warn()

> +		return;
> +	}
> +
> +	/* Calculate notification payload size. */
> +	switch (msg.notify_msg_type) {
> +	case AVS_NOTIFY_FW_READY:
> +		break;
> +
> +	case AVS_NOTIFY_PHRASE_DETECTED:
> +		data_size = sizeof(struct avs_notify_voice_data);
> +		break;
> +
> +	case AVS_NOTIFY_RESOURCE_EVENT:
> +		data_size = sizeof(struct avs_notify_res_data);
> +		break;
> +
> +	case AVS_NOTIFY_MODULE_EVENT:
> +		memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
> +		data_size = sizeof(mod_data) + mod_data.data_size;

it wouldn't hurt to describe the layout behing this formula.

> +		break;
> +
> +	default:
> +		dev_warn(adev->dev, "unknown notification: 0x%08x\n",
> +			 msg.primary);
> +		break;
> +	}
> +
> +	if (data_size) {
> +		data = kmalloc(data_size, GFP_KERNEL);
> +		if (!data)
> +			return;
> +
> +		memcpy_fromio(data, avs_uplink_addr(adev), data_size);
> +	}
> +
> +	/* Perform notification-specific operations. */
> +	switch (msg.notify_msg_type) {
> +	case AVS_NOTIFY_FW_READY:
> +		dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
> +		adev->ipc->ready = true;

avs->ipc->fw_ready?

> +		complete(&adev->fw_ready);> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	kfree(data);
> +}
> +
> +void avs_dsp_process_response(struct avs_dev *adev, u64 header)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +
> +	if (avs_msg_is_reply(header)) {

the naming is confusing, it's difficult for me to understand that a
'response' could not be a 'reply'. The two terms are synonyms, aren't they?

> +		/* Response processing is invoked from IRQ thread. */
> +		spin_lock_irq(&ipc->rx_lock);
> +		avs_dsp_receive_rx(adev, header);
> +		ipc->rx_completed = true;
> +		spin_unlock_irq(&ipc->rx_lock);
> +	} else {
> +		avs_dsp_process_notification(adev, header);
> +	}
> +
> +	complete(&ipc->busy_completion);
> +}
> +
> +irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id)
> +{
> +	struct avs_dev *adev = dev_id;
> +	struct avs_ipc *ipc = adev->ipc;
> +	const struct avs_spec *const spec = adev->spec;
> +	u32 adspis, hipc_rsp, hipc_ack;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS);
> +	if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC))
> +		return ret;
> +
> +	hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc_ack_offset);
> +	hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc_rsp_offset);
> +
> +	/* DSP acked host's request */
> +	if (hipc_ack & spec->hipc_ack_done_mask) {
> +		/* mask done interrupt */
> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
> +				      AVS_ADSP_HIPCCTL_DONE, 0);
> +
> +		complete(&ipc->done_completion);
> +
> +		/* tell DSP it has our attention */
> +		snd_hdac_adsp_updatel(adev, spec->hipc_ack_offset,
> +				      spec->hipc_ack_done_mask,
> +				      spec->hipc_ack_done_mask);
> +		/* unmask done interrupt */
> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
> +				      AVS_ADSP_HIPCCTL_DONE,
> +				      AVS_ADSP_HIPCCTL_DONE);

does the order between the complete() and the next two register updates
matter?

I would have updated the registers immediately and signal the completion
later.

I am also not sure why it's necessary to mask the done interrupt then
unmask it. There is nothing that seems to require this masking?

Or are you expecting the code blocked on wait_for_completion to be
handled with interrupts masked, which could be quite racy?

> +		ret = IRQ_HANDLED;
> +	}
> +
> +	/* DSP sent new response to process */
> +	if (hipc_rsp & spec->hipc_rsp_busy_mask) {
> +		/* mask busy interrupt */
> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
> +				      AVS_ADSP_HIPCCTL_BUSY, 0);
> +
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	return ret;
> +}

> +static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
> +{
> +	int ret;
> +
> +again:
> +	ret = wait_for_completion_timeout(&ipc->busy_completion,
> +					  msecs_to_jiffies(timeout));
> +	/*
> +	 * DSP could be unresponsive at this point e.g. manifested by
> +	 * EXCEPTION_CAUGHT notification. If so, no point in continuing.

EXCEPTION_CAUGHT doesn't seem to be described in this patchset, so not
sure what this comment might mean.

> +	 */
> +	if (!ipc->ready)
> +		return -EPERM;
> +
> +	if (!ret) {
> +		if (!avs_ipc_is_busy(ipc))
> +			return -ETIMEDOUT;
> +		/*
> +		 * Firmware did its job, either notification or reply
> +		 * has been received - now wait until it's processed.
> +		 */
> +		wait_for_completion_killable(&ipc->busy_completion);

can you elaborate on why wait_for_completion() is not enough? I haven't
seen the 'killable' attribute been used by anyone in sound/

> +	}
> +
> +	/* Ongoing notification's bottom-half may cause early wakeup */
> +	spin_lock(&ipc->rx_lock);
> +	if (!ipc->rx_completed) {
> +		/* Reply delayed due to notification. */
> +		reinit_completion(&ipc->busy_completion);
> +		spin_unlock(&ipc->rx_lock);
> +		goto again;

shouldn't there be some counter to avoid potential infinite loops here?

> +	}
> +
> +	spin_unlock(&ipc->rx_lock);
> +	return 0;
> +}

> +static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
> +			       struct avs_ipc_msg *reply, int timeout)
> +{
> +	struct avs_ipc *ipc = adev->ipc;
> +	int ret;
> +
> +	if (!ipc->ready)
> +		return -EPERM;
> +
> +	mutex_lock(&ipc->msg_mutex);
> +
> +	spin_lock(&ipc->rx_lock);
> +	avs_ipc_msg_init(ipc, reply);
> +	avs_dsp_send_tx(adev, request);
> +	spin_unlock(&ipc->rx_lock);
> +
> +	ret = avs_ipc_wait_busy_completion(ipc, timeout);
> +	if (ret) {
> +		if (ret == -ETIMEDOUT) {
> +			dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n",
> +				 ret);

dev_crit() seems over the top if there is a recovery mechanism

> +
> +			avs_ipc_block(ipc);
> +		}
> +		goto exit;
> +	}
> +
> +	ret = ipc->rx.rsp.status;
> +	if (reply) {
> +		reply->header = ipc->rx.header;
> +		if (reply->data && ipc->rx.size)
> +			memcpy(reply->data, ipc->rx.data, reply->size);
> +	}
> +
> +exit:
> +	mutex_unlock(&ipc->msg_mutex);
> +	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)

the last two arguments are not used. is this intentional?

> +{
> +	return avs_dsp_do_send_msg(adev, request, reply, timeout);
> +}
> +
> +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_send_msg_sequence(adev, request, reply, timeout,
> +					 false, false);
> +}
> +
> +int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
> +		     struct avs_ipc_msg *reply)
> +{
> +	return avs_dsp_send_msg_timeout(adev, request, reply,
> +					adev->ipc->default_timeout_ms);
> +}

is there really a 4-level nesting in your helpers?

avs_dsp_send_msg
  avs_dsp_send_msg_timeout
     avs_dsp_send_msg_sequence
           avs_dsp_do_send_msg

this seems complicated, no?

At the very least you should explain what a message and message sequence
are, and why this is split this way.

> +
> +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);
> +}

so the 'pm' means 'wake-d0i0'? that's far from intuitive.

avs_dsp_send_d0i0_msg_timeout() would better describe what you are
trying to do.

In addition you need an explanation that d0i0 is a *firmware* concept
without direct links to the *device* Dx status.

> +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);
> +}

> +void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
> +{
> +	const struct avs_spec *const spec = adev->spec;
> +	u32 value;
> +
> +	value = enable ? AVS_ADSP_ADSPIC_IPC : 0;
> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC,
> +			      AVS_ADSP_ADSPIC_IPC, value);
> +
> +	value = enable ? AVS_ADSP_HIPCCTL_DONE : 0;
> +	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
> +			      AVS_ADSP_HIPCCTL_DONE, value);
> +
> +	value = enable ? AVS_ADSP_HIPCCTL_BUSY : 0;
> +	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
> +			      AVS_ADSP_HIPCCTL_BUSY, value);

does the order matter? please add a comment.



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

* Re: [PATCH 05/17] ASoC: Intel: avs: Add code loading requests
  2022-02-07 12:20 ` [PATCH 05/17] ASoC: Intel: avs: Add code loading requests Cezary Rojewski
@ 2022-02-25  1:02   ` Pierre-Louis Bossart
  2022-02-25 18:08     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:02 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> +#define AVS_CL_TIMEOUT_MS	5000

it's not clear where this comes from, and it's also unclear why both
types of code loading would have the same timeout when the hardware is
so fundamentally different.

> +
> +int avs_ipc_load_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
> +{
> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_MULTIPLE_MODULES);
> +	struct avs_ipc_msg request;
> +	int ret;
> +
> +	msg.load_multi_mods.mod_cnt = num_mod_ids;
> +	request.header = msg.val;
> +	request.data = mod_ids;
> +	request.size = sizeof(*mod_ids) * num_mod_ids;
> +
> +	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "load multiple modules", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_unload_modules(struct avs_dev *adev, u16 *mod_ids, u32 num_mod_ids)
> +{
> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(UNLOAD_MULTIPLE_MODULES);
> +	struct avs_ipc_msg request;
> +	int ret;
> +
> +	msg.load_multi_mods.mod_cnt = num_mod_ids;
> +	request.header = msg.val;
> +	request.data = mod_ids;
> +	request.size = sizeof(*mod_ids) * num_mod_ids;
> +
> +	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "unload multiple modules", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
> +{
> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY);
> +	struct avs_ipc_msg request = {0};

I've asked this before and I don't recall by this case requires an
initialization to zero?

> +	int ret;
> +
> +	msg.load_lib.dma_id = dma_id;
> +	msg.load_lib.lib_id = lib_id;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "load library", ret);
> +
> +	return ret;


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

* Re: [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests
  2022-02-07 12:20 ` [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests Cezary Rojewski
@ 2022-02-25  1:11   ` Pierre-Louis Bossart
  2022-02-25 18:31     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:11 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/7/22 06:20, Cezary Rojewski wrote:
> A 'Pipeline' represents both a container of module instances, and a
> scheduling entity. Multiple pipelines can be bound together to create an
> audio graph. The Pipeline state machine is entirely controlled by IPCs
> (creation, deletion and state changes).

How are the module instances connected within a pipeline? You've said
too much or too little here.


> +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
> +			    u8 instance_id, bool lp, u16 attributes)
> +{
> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.create_ppl.ppl_mem_size = req_size;
> +	msg.create_ppl.ppl_priority = priority;
> +	msg.create_ppl.instance_id = instance_id;
> +	msg.ext.create_ppl.lp = lp;

you may want to describe what the concepts of 'priority', 'lp' and
'attributes' are and which entity defines the values (topology?)

> +	msg.ext.create_ppl.attributes = attributes;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "create pipeline", ret);
> +
> +	return ret;
> +}

>  			u32 val;
> +			/* pipeline management */
> +			struct {
> +				u32 lp:1;
> +				u32 rsvd:3;
> +				u32 attributes:16;
> +			} create_ppl;
> +			struct {
> +				u32 multi_ppl:1;
> +				u32 sync_stop_start:1;

these two are not described at all?

> +			} set_ppl_state;
>  		} ext;
>  	};
>  } __packed;

> +/* Pipeline management messages */
> +enum avs_pipeline_state {
> +	AVS_PPL_STATE_INVALID,
> +	AVS_PPL_STATE_UNINITIALIZED,
> +	AVS_PPL_STATE_RESET,
> +	AVS_PPL_STATE_PAUSED,
> +	AVS_PPL_STATE_RUNNING,
> +	AVS_PPL_STATE_EOS,
> +	AVS_PPL_STATE_ERROR_STOP,
> +	AVS_PPL_STATE_SAVED,
> +	AVS_PPL_STATE_RESTORED,

can you describe that the last two enums might entail and what the
purpose might be?

I can see how the firmware state could be saved in IMR for faster
suspend/resume, but save/restore at the pipeline level doesn't seem to
have an obvious match for an ASoC driver?

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

* Re: [PATCH 07/17] ASoC: Intel: avs: Add module management requests
  2022-02-07 12:20 ` [PATCH 07/17] ASoC: Intel: avs: Add module " Cezary Rojewski
@ 2022-02-25  1:27   ` Pierre-Louis Bossart
  2022-02-25 18:50     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:27 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +			  u8 ppl_id, u8 core_id, u8 domain,

you should explain the relationship between ppl_id and core_id. It seems
that in the same pipeline different modules instances can be pegged to
different cores, which isn't very intuitive given the previous
explanation that a pipeline is a scheduling unit.

The domain as a u8 is not very clear either, I was under the impression
there were only two domains (LL and EDF)?

> +			  void *param, u32 param_size)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
> +	struct avs_ipc_msg request;
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	/* firmware expects size provided in dwords */
> +	msg.ext.init_instance.param_block_size =
> +			DIV_ROUND_UP(param_size, sizeof(u32));
> +	msg.ext.init_instance.ppl_instance_id = ppl_id;
> +	msg.ext.init_instance.core_id = core_id;
> +	msg.ext.init_instance.proc_domain = domain;
> +
> +	request.header = msg.val;
> +	request.data = param;
> +	request.size = param_size;

isn't there a need to check if the module can be initialized? there's
got to be some dependency on pipeline state?

> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "init instance", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "delete instance", ret);
> +
> +	return ret;

same here, can this be used in any pipeline state?

> +}
> +
> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +		 u16 dst_module_id, u8 dst_instance_id,
> +		 u8 dst_queue, u8 src_queue)

what does a queue represent?

> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
> +	msg.ext.bind_unbind.dst_queue = dst_queue;
> +	msg.ext.bind_unbind.src_queue = src_queue;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "bind modules", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
> +		   u16 dst_module_id, u8 dst_instance_id,
> +		   u8 dst_queue, u8 src_queue)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	msg.module_id = module_id;
> +	msg.instance_id = instance_id;
> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
> +	msg.ext.bind_unbind.dst_queue = dst_queue;
> +	msg.ext.bind_unbind.src_queue = src_queue;
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_msg(adev, &request, NULL);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "unbind modules", ret);
> +
> +	return ret;

can this be merged with the bind in a helper, the code looks
quasi-identical with just two lines different.

> +}


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

* Re: [PATCH 08/17] ASoC: Intel: avs: Add power management requests
  2022-02-07 12:20 ` [PATCH 08/17] ASoC: Intel: avs: Add power " Cezary Rojewski
@ 2022-02-25  1:37   ` Pierre-Louis Bossart
  2022-02-25 19:08     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:37 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



> Audio DSP supports low power states i.e.: transitions between D0 and D3
> and D0-substates in form of D0i3. That process is a combination of core

D0i0 and D0i3?

> and IPC operations. Here, Dx and D0ix IPC handlers are added.
> 
> 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/messages.c | 43 ++++++++++++++++++++++++++++++++++
>  sound/soc/intel/avs/messages.h | 16 +++++++++++++
>  2 files changed, 59 insertions(+)
> 
> diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
> index e870d5792a77..1b589689410f 100644
> --- a/sound/soc/intel/avs/messages.c
> +++ b/sound/soc/intel/avs/messages.c
> @@ -347,3 +347,46 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
>  
>  	return 0;
>  }
> +
> +int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX);
> +	struct avs_ipc_msg request;
> +	struct avs_dxstate_info dx;
> +	int ret;
> +
> +	dx.core_mask = core_mask;
> +	dx.dx_mask = powerup ? core_mask : 0;
> +	request.header = msg.val;
> +	request.data = &dx;
> +	request.size = sizeof(dx);
> +
> +	/*
> +	 * SET_D0 is sent for non-main cores only while SET_D3 is used to
> +	 * suspend for all of them. Both cases prevent any D0I3 transitions.

The asymmetry in the comment isn't clear. Did you mean that the main
code is in D0 when powered?

> +	 */
> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "set dx", ret);
> +
> +	return ret;
> +}
> +
> +int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
> +{
> +	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX);
> +	struct avs_ipc_msg request = {0};
> +	int ret;
> +
> +	/* Wake & streaming for < cAVS 2.0 */

I don't how anyone outside of Intel could understand this comment.
Consider explaining what the two terms refer to.

> +	msg.ext.set_d0ix.wake = enable_pg;

simplify the argument? Not sure anyone could understand what wake and
enable_pg mean.

int avs_ipc_set_d0ix(struct avs_dev *adev, bool wake, bool streaming)

> +	msg.ext.set_d0ix.streaming = streaming;
> +
> +	request.header = msg.val;
> +
> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
> +	if (ret)
> +		avs_ipc_err(adev, &request, "set d0ix", ret);
> +
> +	return ret;
> +}
> diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
> index 1dabd1005327..bbdba4631b1f 100644
> --- a/sound/soc/intel/avs/messages.h
> +++ b/sound/soc/intel/avs/messages.h
> @@ -101,6 +101,8 @@ enum avs_module_msg_type {
>  	AVS_MOD_LARGE_CONFIG_SET = 4,
>  	AVS_MOD_BIND = 5,
>  	AVS_MOD_UNBIND = 6,
> +	AVS_MOD_SET_DX = 7,
> +	AVS_MOD_SET_D0IX = 8,
>  	AVS_MOD_DELETE_INSTANCE = 11,
>  };
>  
> @@ -137,6 +139,11 @@ union avs_module_msg {
>  				u32 dst_queue:3;
>  				u32 src_queue:3;
>  			} bind_unbind;
> +			struct {
> +				/* cAVS < 2.0 */
> +				u32 wake:1;
> +				u32 streaming:1;

you probably want to explain how a 'streaming' flag is set at the module
level? One would think all modules connected in a pipeline would need to
use the same flag value.

> +			} set_d0ix;
>  		} ext;
>  	};
>  } __packed;
> @@ -298,4 +305,13 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
>  			     u8 param_id, u8 *request_data, size_t request_size,
>  			     u8 **reply_data, size_t *reply_size);
>  
> +/* DSP cores and domains power management messages */
> +struct avs_dxstate_info {
> +	u32 core_mask;
> +	u32 dx_mask;

what is the convention for D0 and D3 in the mask ? which one is 0 or 1?

Is this also handled in a hierarchical way where only the bits set in
core_mask matter?

> +} __packed;
> +
> +int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup);
> +int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming);
> +
>  #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */

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

* Re: [PATCH 09/17] ASoC: Intel: avs: Add ROM requests
  2022-02-07 12:21 ` [PATCH 09/17] ASoC: Intel: avs: Add ROM requests Cezary Rojewski
@ 2022-02-25  1:42   ` Pierre-Louis Bossart
  2022-02-25 19:19     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:42 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/7/22 06:21, Cezary Rojewski wrote:
> ROM requests are messages initiated by Host to alter firmware early boot
> process. They specify whether the next boot should be a fresh start or if
> IMR can be used to speed things up.

'can' is an imprecise term in technical documentation.

did you mean 'shall' (requirement), 'may' (permission) or 'should'
(recommendation)?

It's my understanding that it's legal to redo a complete reboot from a
clean-slate.

It's also unclear if the firmware can use this mechanism to prevent the
use of IMR, e.g. if in some firmware builds state save/restore was
disabled for some reason.


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

* Re: [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities
  2022-02-07 12:21 ` [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities Cezary Rojewski
@ 2022-02-25  1:53   ` Pierre-Louis Bossart
  2022-02-25 19:20     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  1:53 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/7/22 06:21, Cezary Rojewski wrote:
> With basefw runtime parameter handlers added, implement utility
> functions to ease pipeline and module allocation. IDA is enlisted to
> help with that. Also, as firmware is modular and multiple binaries can
> be loaded throughout the lifetime of a driver, custom firmware caching
> mechanism is added.

It's not clear what the 'lifetime' refers to. Did you mean that the
binaries can be loaded/unloaded on-demand depending on use cases without
having to reboot the DSP, or that at each reboot a different set of
binaries can be used.




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

* Re: [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-07 12:21 ` [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management Cezary Rojewski
@ 2022-02-25  2:02   ` Pierre-Louis Bossart
  2022-02-25 19:27     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  2:02 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
> +{
> +	u32 mask;
> +	int ret;
> +
> +	ret = avs_dsp_core_enable(adev, core_mask);
> +	if (ret < 0)
> +		return ret;
> +
> +	mask = core_mask & ~AVS_MAIN_CORE_MASK;

so here BIT(MAIN_CORE) is zero in mask

> +	if (!mask)
> +		/*
> +		 * without main core, fw is dead anyway
> +		 * so setting D0 for it is futile.

I don't get the comment, you explicitly discarded the main core with
your logical AND above, so this test means that all other non-main cores
are disabled.

> +		 */
> +		return 0;
> +
> +	ret = avs_ipc_set_dx(adev, mask, true);
> +	return AVS_IPC_RET(ret);
> +}
> +
> +static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
> +{
> +	int ret;
> +
> +	ret = avs_ipc_set_dx(adev, core_mask, false);
> +	if (ret)
> +		return AVS_IPC_RET(ret);
> +
> +	return avs_dsp_core_disable(adev, core_mask);
> +}
> +
> +static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
> +{
> +	u32 mask;
> +	int ret;
> +
> +	mask = BIT_MASK(core_id);
> +	if (mask == AVS_MAIN_CORE_MASK)
> +		/* nothing to do for main core */
> +		return 0;
> +	if (core_id >= adev->hw_cfg.dsp_cores) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	adev->core_refs[core_id]++;
> +	if (adev->core_refs[core_id] == 1) {
> +		ret = avs_dsp_enable(adev, mask);
> +		if (ret)
> +			goto err_enable_dsp;
> +	}
> +
> +	return 0;
> +
> +err_enable_dsp:
> +	adev->core_refs[core_id]--;
> +err:
> +	dev_err(adev->dev, "get core failed: %d\n", ret);

you should log which core could not be enabled

> +	return ret;
> +}
> +
> +static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
> +{
> +	u32 mask;
> +	int ret;
> +
> +	mask = BIT_MASK(core_id);
> +	if (mask == AVS_MAIN_CORE_MASK)
> +		/* nothing to do for main core */
> +		return 0;
> +	if (core_id >= adev->hw_cfg.dsp_cores) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	adev->core_refs[core_id]--;
> +	if (!adev->core_refs[core_id]) {
> +		ret = avs_dsp_disable(adev, mask);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	return 0;
> +err:
> +	dev_err(adev->dev, "put core failed: %d\n", ret);

put core %d

> +	return ret;
> +}

>  MODULE_LICENSE("GPL v2");

"GPL"


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

* Re: [PATCH 14/17] ASoC: Intel: avs: General code loading flow
  2022-02-07 12:21 ` [PATCH 14/17] ASoC: Intel: avs: General code loading flow Cezary Rojewski
@ 2022-02-25  2:15   ` Pierre-Louis Bossart
  2022-02-25 19:37     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  2:15 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



> +#define AVS_FW_INIT_TIMEOUT_MS		3000

another timeout?

skl-sst.c:#define SKL_BASEFW_TIMEOUT    300
skl-sst.c:#define SKL_INIT_TIMEOUT      1000
bxt-sst.c:#define BXT_BASEFW_TIMEOUT    3000
cnl-sst.c:#define CNL_INIT_TIMEOUT      300
cnl-sst.c:#define CNL_BASEFW_TIMEOUT    3000

> +#define AVS_ROOT_DIR			"intel/avs"
> +#define AVS_BASEFW_FILENAME		"dsp_basefw.bin"
> +#define AVS_EXT_MANIFEST_MAGIC		0x31454124
> +#define SKL_MANIFEST_MAGIC		0x00000006
> +#define SKL_ADSPFW_OFFSET		0x284
> +
> +static bool debug_ignore_fw_version_check;

this_is_a_very_long_variable_name_isn_t_it?

> +module_param_named(ignore_fw_version, debug_ignore_fw_version_check, bool, 0444);
> +MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes (default), 1=no");

You should clarify the purpose of the version check, and why this driver
needs different firmware binaries and versions than what was already
released to linux-firmware.


> +static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw,
> +					const struct avs_fw_version *min)
> +{
> +	struct avs_fw_manifest *man;
> +	int offset, ret;
> +
> +	ret = avs_fw_ext_manifest_strip(fw);
> +	if (ret)
> +		return ret;
> +
> +	offset = avs_fw_manifest_offset(fw);
> +	if (offset < 0)
> +		return offset;
> +
> +	if (fw->size < offset + sizeof(*man))
> +		return -EINVAL;
> +	if (!min)
> +		return 0;
> +
> +	man = (struct avs_fw_manifest *)(fw->data + offset);
> +	if (man->version.major != min->major ||
> +	    man->version.minor != min->minor ||
> +	    man->version.hotfix != min->hotfix ||
> +	    man->version.build < min->build) {
> +		dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n",
> +			 man->version.major, man->version.minor,
> +			 man->version.hotfix, man->version.build,
> +			 min->major, min->minor, min->hotfix, min->build);

usually when the relevant firmware is not found, the distributions and
users like to see a message informing them of the location of the
firmware binaries.

see thread with Bruce Perens and Jaroslav at
https://github.com/thesofproject/sof-bin/issues/22

> +
> +		if (!debug_ignore_fw_version_check)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int avs_dsp_load_basefw(struct avs_dev *adev)
> +{
> +	const struct avs_fw_version *min_req;
> +	const struct avs_spec *const spec = adev->spec;
> +	const struct firmware *fw;
> +	struct firmware stripped_fw;
> +	char *filename;
> +	int ret;
> +
> +	filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name,
> +			     AVS_BASEFW_FILENAME);
> +	if (!filename)
> +		return -ENOMEM;
> +
> +	ret = avs_request_firmware(adev, &fw, filename);
> +	kfree(filename);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "request firmware failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	stripped_fw = *fw;
> +	min_req = &adev->spec->min_fw_version;
> +
> +	ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "invalid firmware data: %d\n", ret);

should you not release the firmware in all error cases?

if this is handled at a higher level, please add a comment.

> +		return ret;
> +	}
> +
> +	ret = avs_dsp_op(adev, load_basefw, &stripped_fw);
> +	if (ret < 0) {
> +		dev_err(adev->dev, "basefw load failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = wait_for_completion_timeout(&adev->fw_ready,
> +					  msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
> +	if (!ret) {
> +		dev_err(adev->dev, "firmware ready timeout\n");
> +		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}


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

* Re: [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer
  2022-02-07 12:21 ` [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer Cezary Rojewski
@ 2022-02-25  2:18   ` Pierre-Louis Bossart
  2022-02-25 19:38     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  2:18 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma




> +int hda_cldma_stop(struct hda_cldma *cl)
> +{
> +	unsigned int reg;
> +	int ret;
> +
> +	/* disable interrupts */
> +	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
> +			      AVS_ADSP_ADSPIC_CLDMA, 0);

single line?

> +	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);
> +
> +	/* await DMA engine stop */
> +	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg,
> +					 !(reg & SD_CTL_DMA_START),

move to previous line? we can use 100 chars these days, and that's what
you do below.

> +					  AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
> +	cancel_delayed_work_sync(&cl->memcpy_work);
> +
> +	return ret;
> +}

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

* Re: [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA
  2022-02-07 12:21 ` [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA Cezary Rojewski
@ 2022-02-25  2:21   ` Pierre-Louis Bossart
  2022-02-25 19:38     ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  2:21 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> +int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
> +{
> +	struct hda_cldma *cl = &code_loader;
> +	int ret;
> +
> +	hda_cldma_set_data(cl, (void *)lib->data, lib->size);
> +	/* transfer modules manifest */
> +	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
> +	/* DMA id ignored as there is only ever one code-loader DMA */

consider adding new lines to make comments more readable. this is a bit
of an eyesore.

> +	ret = avs_ipc_load_library(adev, 0, id);
> +	hda_cldma_stop(cl);
> +
> +	if (ret) {
> +		ret = AVS_IPC_RET(ret);
> +		dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry)
> +{
> +	struct hda_cldma *cl = &code_loader;
> +	const struct firmware *mod;
> +	char mod_name[128];

use kasprintf?

> +	int ret;
> +
> +	snprintf(mod_name, sizeof(mod_name), "%s/%s/dsp_mod_%pUL.bin",
> +		 AVS_ROOT_DIR, adev->spec->name, mentry->uuid.b);
> +
> +	ret = avs_request_firmware(adev, &mod, mod_name);
> +	if (ret < 0)
> +		return ret;
> +
> +	hda_cldma_set_data(cl, (void *)mod->data, mod->size);
> +	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
> +	ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
> +	hda_cldma_stop(cl);
> +
> +	if (ret) {
> +		dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id,
> +			ret);
> +		return AVS_IPC_RET(ret);
> +	}
> +
> +	return 0;
> +}


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

* Re: [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
  2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
                   ` (17 preceding siblings ...)
  2022-02-21 11:51 ` [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
@ 2022-02-25  2:35 ` Pierre-Louis Bossart
  2022-02-25 15:44   ` Cezary Rojewski
  2022-02-25 18:07   ` Mark Brown
  18 siblings, 2 replies; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25  2:35 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> Note: this series does not add fully functional driver as its size would
> get out of control. Here, focus is put on adding IPC protocol and code
> loading code.

This series is much simpler indeed, see comments in patches, but that
leaves the next steps completely open. It's not quite clear to me how
the previous feedback on trying to up-level the DSP management
functionality might be handled, and if/when you are planning to submit
follow-up patchsets that would implement the required functionality to
at least match what the Skylake driver can do today.

To repeat my previous points: the existing DPCM FE/BE split does not
even being to represent how a DSP might be handled. The BE typically
represents a physical DAI connected to a codec, and the FE pretty much
everything else between a host DMA and the DAI. All the internal format
conversions, mixers and processing are not really represented other than
with DAPM logical widgets, and that's a big miss. There's room for a lot
of improvements that would be of interest to all DSP users.



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

* Re: [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
  2022-02-25  2:35 ` Pierre-Louis Bossart
@ 2022-02-25 15:44   ` Cezary Rojewski
  2022-02-25 16:33     ` Pierre-Louis Bossart
  2022-02-25 18:07   ` Mark Brown
  1 sibling, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 15:44 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 3:35 AM, Pierre-Louis Bossart wrote:
> 
>> Note: this series does not add fully functional driver as its size would
>> get out of control. Here, focus is put on adding IPC protocol and code
>> loading code.
> 
> This series is much simpler indeed, see comments in patches, but that
> leaves the next steps completely open. It's not quite clear to me how
> the previous feedback on trying to up-level the DSP management
> functionality might be handled, and if/when you are planning to submit
> follow-up patchsets that would implement the required functionality to
> at least match what the Skylake driver can do today.
> 
> To repeat my previous points: the existing DPCM FE/BE split does not
> even being to represent how a DSP might be handled. The BE typically
> represents a physical DAI connected to a codec, and the FE pretty much
> everything else between a host DMA and the DAI. All the internal format
> conversions, mixers and processing are not really represented other than
> with DAPM logical widgets, and that's a big miss. There's room for a lot
> of improvements that would be of interest to all DSP users.


Thanks for taking time to provide round of review for the series!

The request was to split the initial series into smaller chunks and 
separate the driver-specific stuff from parts that _could_ get 
incorporated into the framework to level it up in regard to DSP 
management. Series: "Intel: avs: Topology and path management" [1] has 
been provided for such discussion.

Given the request, we are planning to upstream avs-driver in four chunks:
- IPC protocol and code loading (this one)
- Topology and path management [1]
- secondary flows e.g.: DSP recovery
- machine boards


In regard to DPCM FE/BE, ASoC already has DAI-link components: let codec 
operations stay with codec component, leaving DSP related operations as 
platform component responsibility. FE for DSP drivers typically comes 
from topology and drives the HOST DMA part whereas BE deals with LINK 
(hardware, data transfer interface such as PDM or I2S) side, including 
its configuration.
I'm happy to continue the discussion regarding "path" in the dedicated 
series [1] as current series covers IPC protocol and code loading -only.


Regards,
Czarek


[1]: 
https://lore.kernel.org/alsa-devel/20220207132532.3782412-1-cezary.rojewski@intel.com/T/#t

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

* Re: [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
  2022-02-25 15:44   ` Cezary Rojewski
@ 2022-02-25 16:33     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 16:33 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> The request was to split the initial series into smaller chunks and
> separate the driver-specific stuff from parts that _could_ get
> incorporated into the framework to level it up in regard to DSP
> management. Series: "Intel: avs: Topology and path management" [1] has
> been provided for such discussion.
> 
> Given the request, we are planning to upstream avs-driver in four chunks:
> - IPC protocol and code loading (this one)
> - Topology and path management [1]
> - secondary flows e.g.: DSP recovery
> - machine boards
> 
> 
> In regard to DPCM FE/BE, ASoC already has DAI-link components: let codec
> operations stay with codec component, leaving DSP related operations as
> platform component responsibility. FE for DSP drivers typically comes
> from topology and drives the HOST DMA part whereas BE deals with LINK
> (hardware, data transfer interface such as PDM or I2S) side, including
> its configuration.

I respectfully disagree with your analysis, we cannot dissociate DSP and
link management. The intersection between the BE dailink handling and
the DSP management is the configuration of the cpu-dai on the host side.

When the DSP firmware programs the DAI registers, as we do on the Intel
side for SSP, DMIC and ALH/SoundWire, then the format information needs
to be exposed back to the DSP platform driver so that the codec can be
informed of the configuration. Most interfaces can support multiple
formats, and currently we don't have a good way to know what the
firmware changes and how to match PCM hw_params with link configuration.

The current work-around we use is to rely on the dailink fixup to force
the dailink to operate at a rate consistent with the topology, but
that's really not good at all. What would be needed is that all format
changes through the DSP graph are propagated all the way to the DAI and
used for the dailink configuration. That would also enable us to remove
unnecessary SRCs or format conversions, which I believe is a capability
at the heart of your AVS path proposal.

That's really my point, you cannot really think of DSP management
without factoring in DPCM.

It's not just me blabbering into the wind btw, others have voiced the
need to improve FE->BE format handling and add constraints, see

https://lore.kernel.org/alsa-devel/20210323114327.3969072-1-codrin.ciubotariu@microchip.com/

> I'm happy to continue the discussion regarding "path" in the dedicated
> series [1] as current series covers IPC protocol and code loading -only.

this RFC series was not mentioned in the cover letter for this patchset,
so it wouldn't be surprising if others also missed the connection.

> [1]:
> https://lore.kernel.org/alsa-devel/20220207132532.3782412-1-cezary.rojewski@intel.com/T/#t

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

* Re: [PATCH 03/17] ASoC: Intel: Introduce AVS driver
  2022-02-24 23:55   ` Pierre-Louis Bossart
@ 2022-02-25 16:56     ` Cezary Rojewski
  2022-02-25 20:23       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 16:56 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 12:55 AM, Pierre-Louis Bossart wrote:
>> +config SND_SOC_INTEL_AVS
>> +	tristate "Intel AVS driver"
>> +	depends on PCI && ACPI
>> +	depends on COMMON_CLK
>> +	depends on SND_SOC_INTEL_SKYLAKE_FAMILY=n
>> +	default n
> 
> default is already n


Ack.

>> +	select SND_SOC_ACPI
>> +	select SND_HDA_EXT_CORE
>> +	help
>> +	  Enable support for Intel(R) cAVS 1.5 platforms with DSP
>> +	  capabilities. This includes Skylake, Kabylake, Amberlake and
>> +	  Apollolake. This option is mutually exclusive with SKYLAKE
>> +	  driver.
> 
> The feedback from the RFC was that this is not desirable if you want
> anyone to use this driver. The suggested solution was to use the
> intel_dspcfg layer with e.g. dsp_driver=4 for avs. That would allow
> distributions to build this solution for early adopters.


Indeed, this description needs an update!

>> +/* Platform specific descriptor */
>> +struct avs_spec {
>> +	const char *name;
>> +
>> +	const struct avs_dsp_ops *const dops;
> 
> dsp_ops would be clearer. 'd' could refer to just about anything.


Ack.

>> +	const u32 core_init_mask;	/* used during DSP boot */
>> +	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
>> +};
>> +
>> +struct avs_dev {
>> +	struct hda_bus base;
>> +	struct device *dev;
> 
> question: could you directly embed a struct device instead of a pointer,
> that would simplify the conversion through dev_get_drvdata below.
> 
> Unless this *dev is related to the PCI device, in which case you could
> add a comment.


Pointer 'dev' translates to PCI device. No problem adding a comment.

>> +
>> +	void __iomem *adsp_ba;
> 
> I would guess 'ba' is base address? this could be added with comments or
> kernel-doc


Ack.

>> +	const struct avs_spec *spec;
>> +};
>> +
>> +/* from hda_bus to avs_dev */
>> +#define hda_to_avs(hda) container_of(hda, struct avs_dev, base)
>> +/* from hdac_bus to avs_dev */
>> +#define hdac_to_avs(hdac) hda_to_avs(to_hda_bus(hdac))
>> +/* from device to avs_dev */
>> +#define to_avs_dev(dev) \
>> +({ \
>> +	struct hdac_bus *__bus = dev_get_drvdata(dev); \
>> +	hdac_to_avs(__bus); \
>> +})
>> +
>> +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active);
> 
> does this mean 'active' affects all bits in the core_mask? that doesn't
> seem very intuitive.


Can reword to 'power' to match its siblings.

>> +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset);
>> +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
>> +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
>> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
> 
> it's a bit inconsistent to have enable/disable but a boolean for other
> functions?


While there may be a grain of inconsistency, the order of operations 
does not change for power/reset/stall regardless if the operation is 
"positive" or "negative". In _enable() and _disable() case, the order 
does matter with _disable() being the reverse of its counterpart. As 
functions calling either of these already know which one to use, we can 
avoid an if-statement by providing two separate handlers.

>> +#include <linux/module.h>
>> +#include <sound/hdaudio_ext.h>
>> +#include "avs.h"
>> +#include "registers.h"
> 
> consider renaming as avs_registers.h?


This header is for internal use only and is found within directory 
already named 'avs'. "avs.h" header covers a wider range of types and 
that's why its name is generic. All others are specific and thus are not 
prefixed with "avs_".

>> +
>> +#define AVS_ADSPCS_INTERVAL_US		500
>> +#define AVS_ADSPCS_TIMEOUT_US		10000
> 
> these values don't match with anything that was previously used for
> Intel platforms, where the values could be different depending on
> generations.


Im most cases I'm relying on closed-source equivalents.

> bxt-sst.c:#define BXT_BASEFW_TIMEOUT    3000
> bxt-sst.c:#define BXT_ROM_INIT_TIMEOUT  70
> cnl-sst.c:#define CNL_INIT_TIMEOUT      300
> cnl-sst.c:#define CNL_BASEFW_TIMEOUT    3000
> skl-sst-cldma.h:#define SKL_WAIT_TIMEOUT                500     /* 500
> msec */
> skl-sst-dsp.h:#define BXT_INIT_TIMEOUT          300
> skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000
> skl-sst.c:#define SKL_BASEFW_TIMEOUT    300
> skl-sst.c:#define SKL_INIT_TIMEOUT      1000
> 
> please add a comment on how they were determined or align on hardware
> recommendations.


I'm unsure wheather the above is actually correct : )

e.g.: IPC timeout in skylake-drvier are inflated due to incorrect IPC 
protocol handling. Reply sent by firmware may be delayed due to 
unsoliciated notifications and the aforementioned driver did not take 
that into account extending IPC timeout to avoid the problems.

>> +int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool active)
>> +{
>> +	u32 value, mask, reg;
>> +	int ret;
>> +
>> +	mask = AVS_ADSPCS_SPA_MASK(core_mask);
>> +	value = active ? mask : 0;
>> +
>> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
>> +
>> +	mask = AVS_ADSPCS_CPA_MASK(core_mask);
>> +	value = active ? mask : 0;
>> +
>> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
>> +				       reg, (reg & mask) == value,
>> +				       AVS_ADSPCS_INTERVAL_US,
>> +				       AVS_ADSPCS_TIMEOUT_US);
>> +	if (ret)
>> +		dev_err(adev->dev, "core_mask %d %spower failed: %d\n",
>> +			core_mask, active ? "" : "un", ret);
> 
> unpower is an odd wording.


Ack.

>> +
>> +	return ret;
>> +}
>> +
>> +int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
>> +{
>> +	u32 value, mask, reg;
>> +	int ret;
>> +
>> +	mask = AVS_ADSPCS_CRST_MASK(core_mask);
>> +	value = reset ? mask : 0;
>> +
>> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
>> +
>> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
>> +				       reg, (reg & mask) == value,
>> +				       AVS_ADSPCS_INTERVAL_US,
>> +				       AVS_ADSPCS_TIMEOUT_US);
>> +	if (ret)
>> +		dev_err(adev->dev, "core_mask %d %sreset failed: %d\n",
>> +			core_mask, reset ? "" : "un", ret);
> 
> unreset is even more odd. enter reset or exit reset.


Ack.

>> +
>> +	return ret;
>> +}
>> +
>> +int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
>> +{
>> +	u32 value, mask, reg;
>> +	int ret;
>> +
>> +	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
>> +	value = stall ? mask : 0;
>> +
>> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
>> +
>> +	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
>> +				       reg, (reg & mask) == value,
>> +				       AVS_ADSPCS_INTERVAL_US,
>> +				       AVS_ADSPCS_TIMEOUT_US);
>> +	if (ret)
>> +		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
>> +			core_mask, stall ? "" : "un", ret);
> 
> that was probably a copy/paste of stall/unstall in the two cases
> above...this one works, the two above not so much.


Yeah, that's a result of copy/paste. Agree, the wording can be improved.

>> +
>> +	return ret;
>> +}
>> +
>> +int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
>> +{
>> +	int ret;
>> +
>> +	ret = avs_dsp_op(adev, power, core_mask, true);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = avs_dsp_op(adev, reset, core_mask, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return avs_dsp_op(adev, stall, core_mask, false);
>> +}
>> +
>> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
>> +{
>> +	/* Be permissive to allow for full DSP shutdown in disable path. */
> 
> that comment isn't very clear, what is permissive here?


There is no error checking below.

>> +	avs_dsp_op(adev, stall, core_mask, true);
>> +	avs_dsp_op(adev, reset, core_mask, true);
>> +
>> +	return avs_dsp_op(adev, power, core_mask, false);
>> +}
>> +
>> +MODULE_LICENSE("GPL v2");
> 
> "GPL"


Ack.

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

* Re: [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-25  0:56   ` Pierre-Louis Bossart
@ 2022-02-25 18:06     ` Cezary Rojewski
  2022-02-25 20:37       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 18:06 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 1:56 AM, Pierre-Louis Bossart wrote:
>> The boot process involving ROM-code requires specific handling with
>> 'unstall' operations which are not required post-boot with normal IPC so
>> separate set of send-message handlers is added for each of the usecases.
> 
> consider splitting this long sentence and use simpler logic. It's quite
> unclear how you went from boot to use cases.


Agree, ack.

>> +#include "messages.h"
> 
> avs_messages.h?


Same as previously, this header is for internal use only and is found 
within directory  already named 'avs'. "avs.h" header covers a wider 
range of types and that's why its name is generic. All others are 
specific and thus are not prefixed with "avs_".

>>   
>>   struct avs_dev;
>>   
>> @@ -18,6 +19,9 @@ struct avs_dsp_ops {
>>   	int (* const power)(struct avs_dev *, u32, bool);
>>   	int (* const reset)(struct avs_dev *, u32, bool);
>>   	int (* const stall)(struct avs_dev *, u32, bool);
>> +	irqreturn_t (* const irq_handler)(int, void *);
>> +	irqreturn_t (* const irq_thread)(int, void *);
>> +	void (* const int_control)(struct avs_dev *, bool);
> 
> kernel-doc or comments on what the last op might mean?


Sure, will add a comment.

>>   };
>>   
>>   #define avs_dsp_op(adev, op, ...) \
>> @@ -34,6 +38,18 @@ struct avs_spec {
>>   
>>   	const u32 core_init_mask;	/* used during DSP boot */
>>   	const u64 attributes;		/* bitmask of AVS_PLATATTR_* */
>> +	const u32 sram_base_offset;
>> +	const u32 sram_window_size;
>> +
>> +	const u32 rom_status;
>> +	const u32 hipc_req_offset;
>> +	const u32 hipc_req_ext_offset;
>> +	const u32 hipc_req_busy_mask;
>> +	const u32 hipc_ack_offset;
>> +	const u32 hipc_ack_done_mask;
>> +	const u32 hipc_rsp_offset;
>> +	const u32 hipc_rsp_busy_mask;
>> +	const u32 hipc_ctl_offset;
> 
> is this really desirable to describe the IPC registers, when we know
> there were 3 generations of Intel IPC registers. this is ipc-1.5 only.


Indeed, this abstraction could be removed, ack.

>>   };
>>   
>>   struct avs_dev {
>> @@ -42,6 +58,9 @@ struct avs_dev {
>>   
>>   	void __iomem *adsp_ba;
>>   	const struct avs_spec *spec;
>> +	struct avs_ipc *ipc;
>> +
>> +	struct completion fw_ready;
>>   };
>>   
>>   /* from hda_bus to avs_dev */
>> @@ -61,4 +80,78 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
>>   int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask);
>>   int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask);
>>   
>> +/* Inter Process Communication */
>> +
>> +struct avs_ipc_msg {
>> +	union {
>> +		u64 header;
>> +		union avs_global_msg glb;
>> +		union avs_reply_msg rsp;
>> +	};
>> +	void *data;
>> +	size_t size;
>> +};
>> +
>> +struct avs_ipc {
>> +	struct device *dev;
>> +
>> +	struct avs_ipc_msg rx;
>> +	u32 default_timeout_ms;
>> +	bool ready;
> 
> ready for what? This should be described or documented.


In the past this field was called "fw_ready" until we have decided to 
split struct avs_ipc from struct avs_dev. In my opinion "ipc->ready" 
looks very intuitive in the code, given that it translates to: inter 
process communication ready!

No problem with adding a comment though.

>> +
>> +	bool rx_completed;
>> +	spinlock_t rx_lock;
>> +	struct mutex msg_mutex;
> 
> checkpatch would tell you to add a comment for spinlock and mutex. it's
> quite unclear what they might describe and if they are related.


I'll add a kernel-doc for just like for the ->ready field.

>> +	struct completion done_completion;
>> +	struct completion busy_completion;
>> +};
>> +
>> +#define AVS_EIPC	EREMOTEIO
>> +/*
>> + * IPC handlers may return positive value (firmware error code) what denotes
>> + * successful HOST <-> DSP communication yet failure to process specific request.
>> + *
>> + * Below macro converts returned value to linux kernel error code.
>> + * All IPC callers MUST use it as soon as firmware error code is consumed.
>> + */
>> +#define AVS_IPC_RET(ret) \
>> +	(((ret) <= 0) ? (ret) : -AVS_EIPC)
> 
> why not use -EREMOTEIO directly? -AVS_EIPC is not very useful for the
> reader.
> 
> And why -EREMOTEIO? I see that you used it in catpt but that's a very
> surprising code that no one else uses in sound/


Well, the question: "Which kernel error should represent an error coming 
from remote process AKA audio firmware" needed an answer. EREMOTEIO fits 
the description and so it was chosen.

>> +
>> +static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx,
>> +			       const char *name, int error)
>> +{
>> +	/*
>> +	 * If IPC channel is blocked e.g.: due to ongoing recovery,
>> +	 * -EPERM error code is expected and thus it's not an actual error.
>> +	 */
>> +	if (error == -EPERM)
>> +		dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>> +			tx->glb.primary, tx->glb.ext.val, error);
>> +	else
>> +		dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>> +			tx->glb.primary, tx->glb.ext.val, error);
>> +}
> 
> we've used such functions before and the feedback, e.g. from GregKH and
> Mark Brown, has consistenly been that this is pushing the use of dev_dbg
> too far.


In basically all cases the outcome is going to be dev_err(). dev_dbg() 
is here to help keep DSP-recovery scenario viewer-friendly when checking 
dmesg. Ideally, there should be no DSP-recoveries to begin with : )

>> +#define AVS_IPC_TIMEOUT_MS	300
> 
> skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000
> 
> that's one order of magniture lower. please add a comment or align.
> 
>> +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);
>> +
>> +	ipc->rx.header = header;
>> +	if (!msg.status)
>> +		memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
>> +			      ipc->rx.size);
> 
> it wouldn't hurt to describe that the status determines whether
> additional information can be read from a mailbox.


Isn't that consisted with the behaviour of typical API function? Do not 
copy memory and return it to the caller if something went wrong along 
the way?

>> +}
>> +
>> +static void avs_dsp_process_notification(struct avs_dev *adev, u64 header)
>> +{
>> +	struct avs_notify_mod_data mod_data;
>> +	union avs_notify_msg msg = AVS_MSG(header);
>> +	size_t data_size = 0;
>> +	void *data = NULL;
>> +
>> +	if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) {
>> +		dev_dbg(adev->dev, "FW not ready, skip notification: 0x%08x\n",
>> +			msg.primary);
> 
> can this happen?
> 
> you should add a comment on what could be sent before the first 'real'
> sign of life from the DSP.
> 
> it's also unclear why this dev_dbg() when 'unknown notifications' below
> are handled as dev_warn()


I would like to say: "no, this situation cannot happen" very much, but 
that's simply not true. Any notification could be sent prior to FW_READY 
as the internal queue may not always get flushed between the firmware 
restoring.

Ack on the s/warn/info/ part.

>> +		return;
>> +	}
>> +
>> +	/* Calculate notification payload size. */
>> +	switch (msg.notify_msg_type) {
>> +	case AVS_NOTIFY_FW_READY:
>> +		break;
>> +
>> +	case AVS_NOTIFY_PHRASE_DETECTED:
>> +		data_size = sizeof(struct avs_notify_voice_data);
>> +		break;
>> +
>> +	case AVS_NOTIFY_RESOURCE_EVENT:
>> +		data_size = sizeof(struct avs_notify_res_data);
>> +		break;
>> +
>> +	case AVS_NOTIFY_MODULE_EVENT:
>> +		memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data));
>> +		data_size = sizeof(mod_data) + mod_data.data_size;
> 
> it wouldn't hurt to describe the layout behing this formula.


The layout is kind of implied by the structure itself but a comment 
wouldn't hurt, agree.

>> +		break;
>> +
>> +	default:
>> +		dev_warn(adev->dev, "unknown notification: 0x%08x\n",
>> +			 msg.primary);
>> +		break;
>> +	}
>> +
>> +	if (data_size) {
>> +		data = kmalloc(data_size, GFP_KERNEL);
>> +		if (!data)
>> +			return;
>> +
>> +		memcpy_fromio(data, avs_uplink_addr(adev), data_size);
>> +	}
>> +
>> +	/* Perform notification-specific operations. */
>> +	switch (msg.notify_msg_type) {
>> +	case AVS_NOTIFY_FW_READY:
>> +		dev_dbg(adev->dev, "FW READY 0x%08x\n", msg.primary);
>> +		adev->ipc->ready = true;
> 
> avs->ipc->fw_ready?


As I have explained earlier, this was the case until we have separated 
struct avs_ipc from struct avs_dev. I'll provide a kernel-doc instead.

>> +		complete(&adev->fw_ready);> +		break;
>> +
>> +	default:
>> +		break;
>> +	}
>> +
>> +	kfree(data);
>> +}
>> +
>> +void avs_dsp_process_response(struct avs_dev *adev, u64 header)
>> +{
>> +	struct avs_ipc *ipc = adev->ipc;
>> +
>> +	if (avs_msg_is_reply(header)) {
> 
> the naming is confusing, it's difficult for me to understand that a
> 'response' could not be a 'reply'. The two terms are synonyms, aren't they?


Those two are not the same from the firmware's point of view and thus 
they are not the same here. Response is either a reply or a 
notification. Replies are solicited, a request has been sent beforehand. 
Notifications are unsolicited, you are not sure when exactly and if at 
all they arrive.

Just so I'm not called a heretic later: yes, from English dictionary 
point of view these two words are synonyms. In general, wording found in 
this drivers matches firmware equivalents wherever possible to allow 
developers to switch between these two worlds with minimal adaptation 
period possible.

>> +	/* DSP acked host's request */
>> +	if (hipc_ack & spec->hipc_ack_done_mask) {
>> +		/* mask done interrupt */
>> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>> +				      AVS_ADSP_HIPCCTL_DONE, 0);
>> +
>> +		complete(&ipc->done_completion);
>> +
>> +		/* tell DSP it has our attention */
>> +		snd_hdac_adsp_updatel(adev, spec->hipc_ack_offset,
>> +				      spec->hipc_ack_done_mask,
>> +				      spec->hipc_ack_done_mask);
>> +		/* unmask done interrupt */
>> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>> +				      AVS_ADSP_HIPCCTL_DONE,
>> +				      AVS_ADSP_HIPCCTL_DONE);
> 
> does the order between the complete() and the next two register updates
> matter?
> 
> I would have updated the registers immediately and signal the completion
> later.
> 
> I am also not sure why it's necessary to mask the done interrupt then
> unmask it. There is nothing that seems to require this masking?
> 
> Or are you expecting the code blocked on wait_for_completion to be
> handled with interrupts masked, which could be quite racy?


Given how the things turned out in cAVS, some steps are not always 
required. Here, we have very strict implementation and so interrupt are 
masked.

I'm unsure if relocating complete() to the bottom would bring any 
consequences. And no, the code waiting_for_completion is not expecting 
interrupts to be masked as there is no reply for ROM messages.

>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	/* DSP sent new response to process */
>> +	if (hipc_rsp & spec->hipc_rsp_busy_mask) {
>> +		/* mask busy interrupt */
>> +		snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>> +				      AVS_ADSP_HIPCCTL_BUSY, 0);
>> +
>> +		ret = IRQ_WAKE_THREAD;
>> +	}
>> +
>> +	return ret;
>> +}
> 
>> +static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout)
>> +{
>> +	int ret;
>> +
>> +again:
>> +	ret = wait_for_completion_timeout(&ipc->busy_completion,
>> +					  msecs_to_jiffies(timeout));
>> +	/*
>> +	 * DSP could be unresponsive at this point e.g. manifested by
>> +	 * EXCEPTION_CAUGHT notification. If so, no point in continuing.
> 
> EXCEPTION_CAUGHT doesn't seem to be described in this patchset, so not
> sure what this comment might mean.


Comment describes the circumstances for the communication failures and 
arrival of EXCEPTION_CAUGHT notification is one of them.

>> +	 */
>> +	if (!ipc->ready)
>> +		return -EPERM;
>> +
>> +	if (!ret) {
>> +		if (!avs_ipc_is_busy(ipc))
>> +			return -ETIMEDOUT;
>> +		/*
>> +		 * Firmware did its job, either notification or reply
>> +		 * has been received - now wait until it's processed.
>> +		 */
>> +		wait_for_completion_killable(&ipc->busy_completion);
> 
> can you elaborate on why wait_for_completion() is not enough? I haven't
> seen the 'killable' attribute been used by anyone in sound/


This is connected to how firmware handles messaging i.e. via queue. you 
may get BUSY interrupt caused by a notification while waiting for the 
reply for your request. Being 'disturbed' by a notification does not 
mean firmware is dead, it's just busy and so we wait until previous 
response is processed entirely.

>> +	}
>> +
>> +	/* Ongoing notification's bottom-half may cause early wakeup */
>> +	spin_lock(&ipc->rx_lock);
>> +	if (!ipc->rx_completed) {
>> +		/* Reply delayed due to notification. */
>> +		reinit_completion(&ipc->busy_completion);
>> +		spin_unlock(&ipc->rx_lock);
>> +		goto again;
> 
> shouldn't there be some counter to avoid potential infinite loops here?


This is not a bad idea although the counter is going to be high e.g.: 
128. With DEBUG-level logs enabled you can get ton of messages before 
your reply gets finally sent.

>> +	}
>> +
>> +	spin_unlock(&ipc->rx_lock);
>> +	return 0;
>> +}
> 
>> +static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
>> +			       struct avs_ipc_msg *reply, int timeout)
>> +{
>> +	struct avs_ipc *ipc = adev->ipc;
>> +	int ret;
>> +
>> +	if (!ipc->ready)
>> +		return -EPERM;
>> +
>> +	mutex_lock(&ipc->msg_mutex);
>> +
>> +	spin_lock(&ipc->rx_lock);
>> +	avs_ipc_msg_init(ipc, reply);
>> +	avs_dsp_send_tx(adev, request);
>> +	spin_unlock(&ipc->rx_lock);
>> +
>> +	ret = avs_ipc_wait_busy_completion(ipc, timeout);
>> +	if (ret) {
>> +		if (ret == -ETIMEDOUT) {
>> +			dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n",
>> +				 ret);
> 
> dev_crit() seems over the top if there is a recovery mechanism


There is just one dev_crit() within entire driver and it's there for a 
reason - communication failure is critical and in practice, should never 
occur in any scenario on the production hardware.

>> +
>> +			avs_ipc_block(ipc);
>> +		}
>> +		goto exit;
>> +	}
>> +
>> +	ret = ipc->rx.rsp.status;
>> +	if (reply) {
>> +		reply->header = ipc->rx.header;
>> +		if (reply->data && ipc->rx.size)
>> +			memcpy(reply->data, ipc->rx.data, reply->size);
>> +	}
>> +
>> +exit:
>> +	mutex_unlock(&ipc->msg_mutex);
>> +	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)
> 
> the last two arguments are not used. is this intentional?


Used by the d0ix implementation that is not part of this part. Can relocate.

>> +{
>> +	return avs_dsp_do_send_msg(adev, request, reply, timeout);
>> +}
>> +
>> +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_send_msg_sequence(adev, request, reply, timeout,
>> +					 false, false);
>> +}
>> +
>> +int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request,
>> +		     struct avs_ipc_msg *reply)
>> +{
>> +	return avs_dsp_send_msg_timeout(adev, request, reply,
>> +					adev->ipc->default_timeout_ms);
>> +}
> 
> is there really a 4-level nesting in your helpers?
> 
> avs_dsp_send_msg
>    avs_dsp_send_msg_timeout
>       avs_dsp_send_msg_sequence
>             avs_dsp_do_send_msg
> 
> this seems complicated, no?
> 
> At the very least you should explain what a message and message sequence
> are, and why this is split this way.


With d0ix handling added, it becomes clear why such separation exists. I 
left these parts here to reduce the delta in patches that update this 
code later on. Can simplify here and update the d0ix implementation 
accordingly.

>> +
>> +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);
>> +}
> 
> so the 'pm' means 'wake-d0i0'? that's far from intuitive.
> 
> avs_dsp_send_d0i0_msg_timeout() would better describe what you are
> trying to do.
> 
> In addition you need an explanation that d0i0 is a *firmware* concept
> without direct links to the *device* Dx status.


This goes for both, Dx and D0ix related messages.

>> +void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable)
>> +{
>> +	const struct avs_spec *const spec = adev->spec;
>> +	u32 value;
>> +
>> +	value = enable ? AVS_ADSP_ADSPIC_IPC : 0;
>> +	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPIC,
>> +			      AVS_ADSP_ADSPIC_IPC, value);
>> +
>> +	value = enable ? AVS_ADSP_HIPCCTL_DONE : 0;
>> +	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>> +			      AVS_ADSP_HIPCCTL_DONE, value);
>> +
>> +	value = enable ? AVS_ADSP_HIPCCTL_BUSY : 0;
>> +	snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>> +			      AVS_ADSP_HIPCCTL_BUSY, value);
> 
> does the order matter? please add a comment.


interrupt_control() is only used during probing and teardown procedures 
so the order does not matter. You need all those bits up before firmware 
loading can even begin. And when you are done with firmware, you zero 
these out but and the order does not matter either as again, there's 
none to "talk" to.
Note: ADSPIC_IPC is higher in hierarchy than DONE and BUSY.

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

* Re: [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS
  2022-02-25  2:35 ` Pierre-Louis Bossart
  2022-02-25 15:44   ` Cezary Rojewski
@ 2022-02-25 18:07   ` Mark Brown
  1 sibling, 0 replies; 61+ messages in thread
From: Mark Brown @ 2022-02-25 18:07 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Cezary Rojewski, rad, upstream, harshapriya.n, tiwai, alsa-devel,
	hdegoede, amadeuszx.slawinski, cujomalainey, lma

[-- Attachment #1: Type: text/plain, Size: 655 bytes --]

On Thu, Feb 24, 2022 at 08:35:50PM -0600, Pierre-Louis Bossart wrote:

> leaves the next steps completely open. It's not quite clear to me how
> the previous feedback on trying to up-level the DSP management
> functionality might be handled, and if/when you are planning to submit
> follow-up patchsets that would implement the required functionality to
> at least match what the Skylake driver can do today.

I think it's fine that none of the complicated stuff is considered here,
one of the objectives with splitting things up into multiple serieses is
to ensure that the simpler stuff doesn't obscure the bits that need more
attention paying to them.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 05/17] ASoC: Intel: avs: Add code loading requests
  2022-02-25  1:02   ` Pierre-Louis Bossart
@ 2022-02-25 18:08     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 18:08 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:02 AM, Pierre-Louis Bossart wrote:
> 
>> +#define AVS_CL_TIMEOUT_MS	5000
> 
> it's not clear where this comes from, and it's also unclear why both
> types of code loading would have the same timeout when the hardware is
> so fundamentally different.


I can re-check but most often than not, I'm basing these on proven, 
working, close-source equivalent.

>> +int avs_ipc_load_library(struct avs_dev *adev, u32 dma_id, u32 lib_id)
>> +{
>> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(LOAD_LIBRARY);
>> +	struct avs_ipc_msg request = {0};
> 
> I've asked this before and I don't recall by this case requires an
> initialization to zero?


Fields 'data' and 'size' are also part of struct avs_ipc_msg. We zero 
them out here as there is no payload to be sent for LOAD_LIBRARY IPC.

>> +	int ret;
>> +
>> +	msg.load_lib.dma_id = dma_id;
>> +	msg.load_lib.lib_id = lib_id;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg_timeout(adev, &request, NULL, AVS_CL_TIMEOUT_MS);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "load library", ret);
>> +
>> +	return ret;
> 

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

* Re: [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests
  2022-02-25  1:11   ` Pierre-Louis Bossart
@ 2022-02-25 18:31     ` Cezary Rojewski
  2022-02-25 20:42       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 18:31 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:11 AM, Pierre-Louis Bossart wrote:
> On 2/7/22 06:20, Cezary Rojewski wrote:
>> A 'Pipeline' represents both a container of module instances, and a
>> scheduling entity. Multiple pipelines can be bound together to create an
>> audio graph. The Pipeline state machine is entirely controlled by IPCs
>> (creation, deletion and state changes).
> 
> How are the module instances connected within a pipeline? You've said
> too much or too little here.


Hmm.. I doubt commit messages is the place to bring up entire FW 
specification. A high level description is provided to give a 
maintainer/reviewer idea of what the pipeline is. Perhaps s/module 
instances/modules/ would suffice.

>> +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
>> +			    u8 instance_id, bool lp, u16 attributes)
>> +{
>> +	union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.create_ppl.ppl_mem_size = req_size;
>> +	msg.create_ppl.ppl_priority = priority;
>> +	msg.create_ppl.instance_id = instance_id;
>> +	msg.ext.create_ppl.lp = lp;
> 
> you may want to describe what the concepts of 'priority', 'lp' and
> 'attributes' are and which entity defines the values (topology?)


These fields match firmware equivalents 1:1 and are part of pipeline 
descriptor excepted by firmware when initializing a pipeline. Handlers 
found in messages.c are responsible for one and only one task only: 
sending a concrete message. Part of the driver that implements PCM 
operations (not part of this series) cares about the topology (where 
these values actually come from) and invokes the necessary IPCs.

>> +	msg.ext.create_ppl.attributes = attributes;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "create pipeline", ret);
>> +
>> +	return ret;
>> +}
> 
>>   			u32 val;
>> +			/* pipeline management */
>> +			struct {
>> +				u32 lp:1;
>> +				u32 rsvd:3;
>> +				u32 attributes:16;
>> +			} create_ppl;
>> +			struct {
>> +				u32 multi_ppl:1;
>> +				u32 sync_stop_start:1;
> 
> these two are not described at all?


Ack.

>> +			} set_ppl_state;
>>   		} ext;
>>   	};
>>   } __packed;
> 
>> +/* Pipeline management messages */
>> +enum avs_pipeline_state {
>> +	AVS_PPL_STATE_INVALID,
>> +	AVS_PPL_STATE_UNINITIALIZED,
>> +	AVS_PPL_STATE_RESET,
>> +	AVS_PPL_STATE_PAUSED,
>> +	AVS_PPL_STATE_RUNNING,
>> +	AVS_PPL_STATE_EOS,
>> +	AVS_PPL_STATE_ERROR_STOP,
>> +	AVS_PPL_STATE_SAVED,
>> +	AVS_PPL_STATE_RESTORED,
> 
> can you describe that the last two enums might entail and what the
> purpose might be?
> 
> I can see how the firmware state could be saved in IMR for faster
> suspend/resume, but save/restore at the pipeline level doesn't seem to
> have an obvious match for an ASoC driver?


The enum lists all available pipeline states. We're planning to move 
these to uapi later on to allow apps to monitor running pipelines states 
real-time.

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

* Re: [PATCH 07/17] ASoC: Intel: avs: Add module management requests
  2022-02-25  1:27   ` Pierre-Louis Bossart
@ 2022-02-25 18:50     ` Cezary Rojewski
  2022-02-25 20:44       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 18:50 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +			  u8 ppl_id, u8 core_id, u8 domain,
> 
> you should explain the relationship between ppl_id and core_id. It seems
> that in the same pipeline different modules instances can be pegged to
> different cores, which isn't very intuitive given the previous
> explanation that a pipeline is a scheduling unit.
> 
> The domain as a u8 is not very clear either, I was under the impression
> there were only two domains (LL and EDF)?


Hmm.. such explanations are supposed to be part of HW or FW 
specifications. I don't believe kernel is a place for that. Fields found 
here are needed to provide all the necessary information firmware 
expects when requesting INIT_INSTANCE. What's possible and how's 
everything handled internally is for firmware to decide and explain. 
There are no if-statements in the driver's code that force 
ppl_id/core_id relation so I don't see why reader would get an 
impression there is some dependency. What's in the topology gets routed 
to firmware with help of above function.

Just to confirm: yes, you can have multiple cores engaged in servicing 
modules found in single pipelines.

In regard to field name/sizes: again, these match firmware equivalents 
1:1 so it's easy to switch back and forth.

>> +			  void *param, u32 param_size)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>> +	struct avs_ipc_msg request;
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	/* firmware expects size provided in dwords */
>> +	msg.ext.init_instance.param_block_size =
>> +			DIV_ROUND_UP(param_size, sizeof(u32));
>> +	msg.ext.init_instance.ppl_instance_id = ppl_id;
>> +	msg.ext.init_instance.core_id = core_id;
>> +	msg.ext.init_instance.proc_domain = domain;
>> +
>> +	request.header = msg.val;
>> +	request.data = param;
>> +	request.size = param_size;
> 
> isn't there a need to check if the module can be initialized? there's
> got to be some dependency on pipeline state?


IPC handlers found in message.c have one and only one purpose only: send 
a message. Firmware will return an error if arguments passed are invalid.

Also, note that ALSA/ASoC already have a working state machine for 
streaming. There is no reason to re-implement it here.

>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "init instance", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8 instance_id)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "delete instance", ret);
>> +
>> +	return ret;
> 
> same here, can this be used in any pipeline state?


Ditto.

>> +}
>> +
>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +		 u16 dst_module_id, u8 dst_instance_id,
>> +		 u8 dst_queue, u8 src_queue)
> 
> what does a queue represent?


In firmware's nomenclature pin/index/queue are synonyms when speaking 
about module instances.

>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
>> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>> +	msg.ext.bind_unbind.dst_queue = dst_queue;
>> +	msg.ext.bind_unbind.src_queue = src_queue;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "bind modules", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int avs_ipc_unbind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>> +		   u16 dst_module_id, u8 dst_instance_id,
>> +		   u8 dst_queue, u8 src_queue)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(UNBIND);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	msg.module_id = module_id;
>> +	msg.instance_id = instance_id;
>> +	msg.ext.bind_unbind.dst_module_id = dst_module_id;
>> +	msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>> +	msg.ext.bind_unbind.dst_queue = dst_queue;
>> +	msg.ext.bind_unbind.src_queue = src_queue;
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_msg(adev, &request, NULL);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "unbind modules", ret);
>> +
>> +	return ret;
> 
> can this be merged with the bind in a helper, the code looks
> quasi-identical with just two lines different.


We had these two coupled together in the past just like you mention. 
Lately we'd decided that having two if-statements (one for message type 
and the other for error message) just two reduce file by just few lines 
is not worth it. So we chose the readability over small file-size gain.

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

* Re: [PATCH 08/17] ASoC: Intel: avs: Add power management requests
  2022-02-25  1:37   ` Pierre-Louis Bossart
@ 2022-02-25 19:08     ` Cezary Rojewski
  2022-02-25 20:46       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:08 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:37 AM, Pierre-Louis Bossart wrote:
>> Audio DSP supports low power states i.e.: transitions between D0 and D3
>> and D0-substates in form of D0i3. That process is a combination of core
> 
> D0i0 and D0i3?


Ack.

>> and IPC operations. Here, Dx and D0ix IPC handlers are added.
>>
>> 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/messages.c | 43 ++++++++++++++++++++++++++++++++++
>>   sound/soc/intel/avs/messages.h | 16 +++++++++++++
>>   2 files changed, 59 insertions(+)
>>
>> diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
>> index e870d5792a77..1b589689410f 100644
>> --- a/sound/soc/intel/avs/messages.c
>> +++ b/sound/soc/intel/avs/messages.c
>> @@ -347,3 +347,46 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
>>   
>>   	return 0;
>>   }
>> +
>> +int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_DX);
>> +	struct avs_ipc_msg request;
>> +	struct avs_dxstate_info dx;
>> +	int ret;
>> +
>> +	dx.core_mask = core_mask;
>> +	dx.dx_mask = powerup ? core_mask : 0;
>> +	request.header = msg.val;
>> +	request.data = &dx;
>> +	request.size = sizeof(dx);
>> +
>> +	/*
>> +	 * SET_D0 is sent for non-main cores only while SET_D3 is used to
>> +	 * suspend for all of them. Both cases prevent any D0I3 transitions.
> 
> The asymmetry in the comment isn't clear. Did you mean that the main
> code is in D0 when powered?


Yes. There is no putting MAIN_CORE to D0 as we must be in D0 to begin 
with, if we're thinking about sending an IPC to the base firmware.

>> +	 */
>> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, true);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "set dx", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming)
>> +{
>> +	union avs_module_msg msg = AVS_MODULE_REQUEST(SET_D0IX);
>> +	struct avs_ipc_msg request = {0};
>> +	int ret;
>> +
>> +	/* Wake & streaming for < cAVS 2.0 */
> 
> I don't how anyone outside of Intel could understand this comment.
> Consider explaining what the two terms refer to.


Sure, will improve the wording.

>> +	msg.ext.set_d0ix.wake = enable_pg;
> 
> simplify the argument? Not sure anyone could understand what wake and
> enable_pg mean.


Well, CG and PG are popular shortcuts among Intel audio team and stand 
for clock gating and power gating respectively. 'wake' is firmware 
specific. I can provide a comment, but not all question are going to be 
answered by it. Firmware specification is the place to find the answer 
for most of these.

> int avs_ipc_set_d0ix(struct avs_dev *adev, bool wake, bool streaming)
> 
>> +	msg.ext.set_d0ix.streaming = streaming;
>> +
>> +	request.header = msg.val;
>> +
>> +	ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
>> +	if (ret)
>> +		avs_ipc_err(adev, &request, "set d0ix", ret);
>> +
>> +	return ret;
>> +}
>> diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
>> index 1dabd1005327..bbdba4631b1f 100644
>> --- a/sound/soc/intel/avs/messages.h
>> +++ b/sound/soc/intel/avs/messages.h
>> @@ -101,6 +101,8 @@ enum avs_module_msg_type {
>>   	AVS_MOD_LARGE_CONFIG_SET = 4,
>>   	AVS_MOD_BIND = 5,
>>   	AVS_MOD_UNBIND = 6,
>> +	AVS_MOD_SET_DX = 7,
>> +	AVS_MOD_SET_D0IX = 8,
>>   	AVS_MOD_DELETE_INSTANCE = 11,
>>   };
>>   
>> @@ -137,6 +139,11 @@ union avs_module_msg {
>>   				u32 dst_queue:3;
>>   				u32 src_queue:3;
>>   			} bind_unbind;
>> +			struct {
>> +				/* cAVS < 2.0 */
>> +				u32 wake:1;
>> +				u32 streaming:1;
> 
> you probably want to explain how a 'streaming' flag is set at the module
> level? One would think all modules connected in a pipeline would need to
> use the same flag value.


Some of the fields are confusing and I agree, but driver has to adhere 
to FW expectations if it wants to be a working one. I would like to 
avoid judging the firmware architecture here, regardless of how 
confusing we think it is.

'wake' and 'streaming' fields are part of SET_D0ix message is which part 
of MODULE-type message interface. Base firmware is, from architecture 
point of view, a module of type=0 (module_id) and instance id=0 
(instance_id).

>> +			} set_d0ix;
>>   		} ext;
>>   	};
>>   } __packed;
>> @@ -298,4 +305,13 @@ int avs_ipc_get_large_config(struct avs_dev *adev, u16 module_id, u8 instance_id
>>   			     u8 param_id, u8 *request_data, size_t request_size,
>>   			     u8 **reply_data, size_t *reply_size);
>>   
>> +/* DSP cores and domains power management messages */
>> +struct avs_dxstate_info {
>> +	u32 core_mask;
>> +	u32 dx_mask;
> 
> what is the convention for D0 and D3 in the mask ? which one is 0 or 1?
> 
> Is this also handled in a hierarchical way where only the bits set in
> core_mask matter?


Can provide a short kernel-doc for these two, sure.

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

* Re: [PATCH 09/17] ASoC: Intel: avs: Add ROM requests
  2022-02-25  1:42   ` Pierre-Louis Bossart
@ 2022-02-25 19:19     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:19 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:42 AM, Pierre-Louis Bossart wrote:
> On 2/7/22 06:21, Cezary Rojewski wrote:
>> ROM requests are messages initiated by Host to alter firmware early boot
>> process. They specify whether the next boot should be a fresh start or if
>> IMR can be used to speed things up.
> 
> 'can' is an imprecise term in technical documentation.
> 
> did you mean 'shall' (requirement), 'may' (permission) or 'should'
> (recommendation)?
> 
> It's my understanding that it's legal to redo a complete reboot from a
> clean-slate.
> 
> It's also unclear if the firmware can use this mechanism to prevent the
> use of IMR, e.g. if in some firmware builds state save/restore was
> disabled for some reason.


This is a very good feedback, indeed I miss out on the 'can' vs 'shall' 
(and similar) quite often when speaking about hw/fw procedures. Thank 
you for paying attention, Pierre.

Yes, in this case driver has total control over the procedure and 
chooses which method to use when booting the DSP.

While I can't say the details, closed-source firmware (from APL and 
onwards) is built with IMR in mind. By that I mean that it assumes IMR 
is always present. So I believe such scenario - having save/restore 
disabled - is not possible here.

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

* Re: [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities
  2022-02-25  1:53   ` Pierre-Louis Bossart
@ 2022-02-25 19:20     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:20 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 2:53 AM, Pierre-Louis Bossart wrote:
> On 2/7/22 06:21, Cezary Rojewski wrote:
>> With basefw runtime parameter handlers added, implement utility
>> functions to ease pipeline and module allocation. IDA is enlisted to
>> help with that. Also, as firmware is modular and multiple binaries can
>> be loaded throughout the lifetime of a driver, custom firmware caching
>> mechanism is added.
> 
> It's not clear what the 'lifetime' refers to. Did you mean that the
> binaries can be loaded/unloaded on-demand depending on use cases without
> having to reboot the DSP, or that at each reboot a different set of
> binaries can be used.

Ack on improving the commit's message. Thanks for feedback!

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

* Re: [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-25  2:02   ` Pierre-Louis Bossart
@ 2022-02-25 19:27     ` Cezary Rojewski
  2022-02-25 20:21       ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:27 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 3:02 AM, Pierre-Louis Bossart wrote:
> 
>> +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
>> +{
>> +	u32 mask;
>> +	int ret;
>> +
>> +	ret = avs_dsp_core_enable(adev, core_mask);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	mask = core_mask & ~AVS_MAIN_CORE_MASK;
> 
> so here BIT(MAIN_CORE) is zero in mask


What's wrong with AVS_MAIN_CORE_MASK being used explicitly?

>> +	if (!mask)
>> +		/*
>> +		 * without main core, fw is dead anyway
>> +		 * so setting D0 for it is futile.
> 
> I don't get the comment, you explicitly discarded the main core with
> your logical AND above, so this test means that all other non-main cores
> are disabled.

There is no setting D0 for MAIN_CORE as firmware is not operational 
without it. Firmware needs to be notified about D3 -> D0 transitions 
only in case of non-MAIN_COREs.


>> +		 */
>> +		return 0;
>> +
>> +	ret = avs_ipc_set_dx(adev, mask, true);
>> +	return AVS_IPC_RET(ret);
>> +}
>> +
>> +static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
>> +{
>> +	int ret;
>> +
>> +	ret = avs_ipc_set_dx(adev, core_mask, false);
>> +	if (ret)
>> +		return AVS_IPC_RET(ret);
>> +
>> +	return avs_dsp_core_disable(adev, core_mask);
>> +}
>> +
>> +static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
>> +{
>> +	u32 mask;
>> +	int ret;
>> +
>> +	mask = BIT_MASK(core_id);
>> +	if (mask == AVS_MAIN_CORE_MASK)
>> +		/* nothing to do for main core */
>> +		return 0;
>> +	if (core_id >= adev->hw_cfg.dsp_cores) {
>> +		ret = -EINVAL;
>> +		goto err;
>> +	}
>> +
>> +	adev->core_refs[core_id]++;
>> +	if (adev->core_refs[core_id] == 1) {
>> +		ret = avs_dsp_enable(adev, mask);
>> +		if (ret)
>> +			goto err_enable_dsp;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_enable_dsp:
>> +	adev->core_refs[core_id]--;
>> +err:
>> +	dev_err(adev->dev, "get core failed: %d\n", ret);
> 
> you should log which core could not be enabled


Good catch! Ack.

>> +	return ret;
>> +}
>> +
>> +static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
>> +{
>> +	u32 mask;
>> +	int ret;
>> +
>> +	mask = BIT_MASK(core_id);
>> +	if (mask == AVS_MAIN_CORE_MASK)
>> +		/* nothing to do for main core */
>> +		return 0;
>> +	if (core_id >= adev->hw_cfg.dsp_cores) {
>> +		ret = -EINVAL;
>> +		goto err;
>> +	}
>> +
>> +	adev->core_refs[core_id]--;
>> +	if (!adev->core_refs[core_id]) {
>> +		ret = avs_dsp_disable(adev, mask);
>> +		if (ret)
>> +			goto err;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	dev_err(adev->dev, "put core failed: %d\n", ret);
> 
> put core %d


Ack.

>> +	return ret;
>> +}
> 
>>   MODULE_LICENSE("GPL v2");
> 
> "GPL"


Ack.

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

* Re: [PATCH 14/17] ASoC: Intel: avs: General code loading flow
  2022-02-25  2:15   ` Pierre-Louis Bossart
@ 2022-02-25 19:37     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:37 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 3:15 AM, Pierre-Louis Bossart wrote:
> 
> 
>> +#define AVS_FW_INIT_TIMEOUT_MS		3000
> 
> another timeout?


Different operation, different timeout. What's wrong with that? And we 
are not repeating it three times (skl/bxt/cnl) as it's done in 
skylake-driver!

> skl-sst.c:#define SKL_BASEFW_TIMEOUT    300
> skl-sst.c:#define SKL_INIT_TIMEOUT      1000
> bxt-sst.c:#define BXT_BASEFW_TIMEOUT    3000
> cnl-sst.c:#define CNL_INIT_TIMEOUT      300
> cnl-sst.c:#define CNL_BASEFW_TIMEOUT    3000
> 
>> +#define AVS_ROOT_DIR			"intel/avs"
>> +#define AVS_BASEFW_FILENAME		"dsp_basefw.bin"
>> +#define AVS_EXT_MANIFEST_MAGIC		0x31454124
>> +#define SKL_MANIFEST_MAGIC		0x00000006
>> +#define SKL_ADSPFW_OFFSET		0x284
>> +
>> +static bool debug_ignore_fw_version_check;
> 
> this_is_a_very_long_variable_name_isn_t_it?


Can drop "_check", dropping anything else makes this ambiguous. Also, 
this variable is used just once, in the very line below.

>> +module_param_named(ignore_fw_version, debug_ignore_fw_version_check, bool, 0444);
>> +MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes (default), 1=no");
> 
> You should clarify the purpose of the version check, and why this driver
> needs different firmware binaries and versions than what was already
> released to linux-firmware.


Ack. Indeed the name by itself may not be sufficient.

>> +static int avs_fw_manifest_strip_verify(struct avs_dev *adev, struct firmware *fw,
>> +					const struct avs_fw_version *min)
>> +{
>> +	struct avs_fw_manifest *man;
>> +	int offset, ret;
>> +
>> +	ret = avs_fw_ext_manifest_strip(fw);
>> +	if (ret)
>> +		return ret;
>> +
>> +	offset = avs_fw_manifest_offset(fw);
>> +	if (offset < 0)
>> +		return offset;
>> +
>> +	if (fw->size < offset + sizeof(*man))
>> +		return -EINVAL;
>> +	if (!min)
>> +		return 0;
>> +
>> +	man = (struct avs_fw_manifest *)(fw->data + offset);
>> +	if (man->version.major != min->major ||
>> +	    man->version.minor != min->minor ||
>> +	    man->version.hotfix != min->hotfix ||
>> +	    man->version.build < min->build) {
>> +		dev_warn(adev->dev, "bad FW version %d.%d.%d.%d, expected %d.%d.%d.%d or newer\n",
>> +			 man->version.major, man->version.minor,
>> +			 man->version.hotfix, man->version.build,
>> +			 min->major, min->minor, min->hotfix, min->build);
> 
> usually when the relevant firmware is not found, the distributions and
> users like to see a message informing them of the location of the
> firmware binaries.
> 
> see thread with Bruce Perens and Jaroslav at
> https://github.com/thesofproject/sof-bin/issues/22


That's a helpful link and a good conversation, thank you. I see that 
Bruce mentions /lib/firmware as 3rd in presented order and that's where 
we intend to have firmware files. We are not planning to have a separate 
repo for base firmware/library binaries if that's the question.

Given the above, I'm not sure if we should be mentioning the need to 
update /lib/firmware explicitly as people already recognize it as one of 
the default locations to check.

>> +
>> +		if (!debug_ignore_fw_version_check)
>> +			return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int avs_dsp_load_basefw(struct avs_dev *adev)
>> +{
>> +	const struct avs_fw_version *min_req;
>> +	const struct avs_spec *const spec = adev->spec;
>> +	const struct firmware *fw;
>> +	struct firmware stripped_fw;
>> +	char *filename;
>> +	int ret;
>> +
>> +	filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, spec->name,
>> +			     AVS_BASEFW_FILENAME);
>> +	if (!filename)
>> +		return -ENOMEM;
>> +
>> +	ret = avs_request_firmware(adev, &fw, filename);
>> +	kfree(filename);
>> +	if (ret < 0) {
>> +		dev_err(adev->dev, "request firmware failed: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	stripped_fw = *fw;
>> +	min_req = &adev->spec->min_fw_version;
>> +
>> +	ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, min_req);
>> +	if (ret < 0) {
>> +		dev_err(adev->dev, "invalid firmware data: %d\n", ret);
> 
> should you not release the firmware in all error cases?
> 
> if this is handled at a higher level, please add a comment.


I'll sync with Amadeo regarding this. It seems it's cleared only on 
->remove().

>> +		return ret;
>> +	}
>> +
>> +	ret = avs_dsp_op(adev, load_basefw, &stripped_fw);
>> +	if (ret < 0) {
>> +		dev_err(adev->dev, "basefw load failed: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = wait_for_completion_timeout(&adev->fw_ready,
>> +					  msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS));
>> +	if (!ret) {
>> +		dev_err(adev->dev, "firmware ready timeout\n");
>> +		avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	return 0;
>> +}
> 

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

* Re: [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer
  2022-02-25  2:18   ` Pierre-Louis Bossart
@ 2022-02-25 19:38     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:38 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 3:18 AM, Pierre-Louis Bossart wrote:
>> +int hda_cldma_stop(struct hda_cldma *cl)
>> +{
>> +	unsigned int reg;
>> +	int ret;
>> +
>> +	/* disable interrupts */
>> +	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC,
>> +			      AVS_ADSP_ADSPIC_CLDMA, 0);
> 
> single line?
> 
>> +	snd_hdac_stream_updateb(cl, SD_CTL, SD_INT_MASK | SD_CTL_DMA_START, 0);
>> +
>> +	/* await DMA engine stop */
>> +	ret = snd_hdac_stream_readb_poll(cl, SD_CTL, reg,
>> +					 !(reg & SD_CTL_DMA_START),
> 
> move to previous line? we can use 100 chars these days, and that's what
> you do below.
> 
>> +					  AVS_CL_OP_INTERVAL_US, AVS_CL_OP_TIMEOUT_US);
>> +	cancel_delayed_work_sync(&cl->memcpy_work);
>> +
>> +	return ret;
>> +}


Ack on both.

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

* Re: [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA
  2022-02-25  2:21   ` Pierre-Louis Bossart
@ 2022-02-25 19:38     ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-25 19:38 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 3:21 AM, Pierre-Louis Bossart wrote:
> 
>> +int avs_cldma_load_library(struct avs_dev *adev, struct firmware *lib, u32 id)
>> +{
>> +	struct hda_cldma *cl = &code_loader;
>> +	int ret;
>> +
>> +	hda_cldma_set_data(cl, (void *)lib->data, lib->size);
>> +	/* transfer modules manifest */
>> +	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
>> +	/* DMA id ignored as there is only ever one code-loader DMA */
> 
> consider adding new lines to make comments more readable. this is a bit
> of an eyesore.


Ack.

>> +	ret = avs_ipc_load_library(adev, 0, id);
>> +	hda_cldma_stop(cl);
>> +
>> +	if (ret) {
>> +		ret = AVS_IPC_RET(ret);
>> +		dev_err(adev->dev, "transfer lib %d failed: %d\n", id, ret);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int avs_cldma_load_module(struct avs_dev *adev, struct avs_module_entry *mentry)
>> +{
>> +	struct hda_cldma *cl = &code_loader;
>> +	const struct firmware *mod;
>> +	char mod_name[128];
> 
> use kasprintf?


Good idea, ack!

>> +	int ret;
>> +
>> +	snprintf(mod_name, sizeof(mod_name), "%s/%s/dsp_mod_%pUL.bin",
>> +		 AVS_ROOT_DIR, adev->spec->name, mentry->uuid.b);
>> +
>> +	ret = avs_request_firmware(adev, &mod, mod_name);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	hda_cldma_set_data(cl, (void *)mod->data, mod->size);
>> +	hda_cldma_transfer(cl, msecs_to_jiffies(AVS_CLDMA_START_DELAY_MS));
>> +	ret = avs_ipc_load_modules(adev, &mentry->module_id, 1);
>> +	hda_cldma_stop(cl);
>> +
>> +	if (ret) {
>> +		dev_err(adev->dev, "load module %d failed: %d\n", mentry->module_id,
>> +			ret);
>> +		return AVS_IPC_RET(ret);
>> +	}
>> +
>> +	return 0;
>> +}
> 

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

* Re: [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-25 19:27     ` Cezary Rojewski
@ 2022-02-25 20:21       ` Pierre-Louis Bossart
  2022-02-28 15:30         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:21 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma




>>> +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
>>> +{
>>> +    u32 mask;
>>> +    int ret;
>>> +
>>> +    ret = avs_dsp_core_enable(adev, core_mask);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    mask = core_mask & ~AVS_MAIN_CORE_MASK;
>>
>> so here BIT(MAIN_CORE) is zero in mask
> 
> 
> What's wrong with AVS_MAIN_CORE_MASK being used explicitly?
> 
>>> +    if (!mask)
>>> +        /*
>>> +         * without main core, fw is dead anyway
>>> +         * so setting D0 for it is futile.
>>
>> I don't get the comment, you explicitly discarded the main core with
>> your logical AND above, so this test means that all other non-main cores
>> are disabled.
> 
> There is no setting D0 for MAIN_CORE as firmware is not operational
> without it. Firmware needs to be notified about D3 -> D0 transitions
> only in case of non-MAIN_COREs.

the comment was about 'without main core'.

This is difficult to follow, because you've discarded the main code in
the if (!mask), so that's an always-true case, which makes the rest of
the explanations not so clear.

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

* Re: [PATCH 03/17] ASoC: Intel: Introduce AVS driver
  2022-02-25 16:56     ` Cezary Rojewski
@ 2022-02-25 20:23       ` Pierre-Louis Bossart
  2022-02-28 14:52         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:23 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


>>> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
>>> +{
>>> +    /* Be permissive to allow for full DSP shutdown in disable path. */
>>
>> that comment isn't very clear, what is permissive here?
> 
> 
> There is no error checking below.
> 
>>> +    avs_dsp_op(adev, stall, core_mask, true);
>>> +    avs_dsp_op(adev, reset, core_mask, true);
>>> +
>>> +    return avs_dsp_op(adev, power, core_mask, false);

consider adding a comment then, along the lines of 'we don't prevent
suspend or shutdown with error checks' or something. 'permissive' was
rather unclear to me.

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

* Re: [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-25 18:06     ` Cezary Rojewski
@ 2022-02-25 20:37       ` Pierre-Louis Bossart
  2022-02-28 15:19         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:37 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


>>> +static inline void avs_ipc_err(struct avs_dev *adev, struct
>>> avs_ipc_msg *tx,
>>> +                   const char *name, int error)
>>> +{
>>> +    /*
>>> +     * If IPC channel is blocked e.g.: due to ongoing recovery,
>>> +     * -EPERM error code is expected and thus it's not an actual error.
>>> +     */
>>> +    if (error == -EPERM)
>>> +        dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>>> +            tx->glb.primary, tx->glb.ext.val, error);
>>> +    else
>>> +        dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>>> +            tx->glb.primary, tx->glb.ext.val, error);
>>> +}
>>
>> we've used such functions before and the feedback, e.g. from GregKH and
>> Mark Brown, has consistenly been that this is pushing the use of dev_dbg
>> too far.
> 
> 
> In basically all cases the outcome is going to be dev_err(). dev_dbg()
> is here to help keep DSP-recovery scenario viewer-friendly when checking
> dmesg. Ideally, there should be no DSP-recoveries to begin with : )

I will refer you to this thread:

https://lore.kernel.org/alsa-devel/YGX5AUQi41z52xk8@kroah.com/

> 
>>> +#define AVS_IPC_TIMEOUT_MS    300
>>
>> skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000
>>
>> that's one order of magniture lower. please add a comment or align.
>>
>>> +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);
>>> +
>>> +    ipc->rx.header = header;
>>> +    if (!msg.status)
>>> +        memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
>>> +                  ipc->rx.size);
>>
>> it wouldn't hurt to describe that the status determines whether
>> additional information can be read from a mailbox.
> 
> 
> Isn't that consisted with the behaviour of typical API function? Do not
> copy memory and return it to the caller if something went wrong along
> the way?

oh, I thought this was a case where the header contains all the
information, and only in specific cases you need to read stuff from the
mailbox.

You definitively need to add a comment on whether this is an error
handling or a feature.


>>> +void avs_dsp_process_response(struct avs_dev *adev, u64 header)
>>> +{
>>> +    struct avs_ipc *ipc = adev->ipc;
>>> +
>>> +    if (avs_msg_is_reply(header)) {
>>
>> the naming is confusing, it's difficult for me to understand that a
>> 'response' could not be a 'reply'. The two terms are synonyms, aren't
>> they?
> 
> 
> Those two are not the same from the firmware's point of view and thus
> they are not the same here. Response is either a reply or a
> notification. Replies are solicited, a request has been sent beforehand.
> Notifications are unsolicited, you are not sure when exactly and if at
> all they arrive.

add a comment then.

> Just so I'm not called a heretic later: yes, from English dictionary
> point of view these two words are synonyms. In general, wording found in
> this drivers matches firmware equivalents wherever possible to allow
> developers to switch between these two worlds with minimal adaptation
> period possible.

> 
>>> +    /* DSP acked host's request */
>>> +    if (hipc_ack & spec->hipc_ack_done_mask) {
>>> +        /* mask done interrupt */
>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>> +                      AVS_ADSP_HIPCCTL_DONE, 0);
>>> +
>>> +        complete(&ipc->done_completion);
>>> +
>>> +        /* tell DSP it has our attention */
>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ack_offset,
>>> +                      spec->hipc_ack_done_mask,
>>> +                      spec->hipc_ack_done_mask);
>>> +        /* unmask done interrupt */
>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>> +                      AVS_ADSP_HIPCCTL_DONE,
>>> +                      AVS_ADSP_HIPCCTL_DONE);
>>
>> does the order between the complete() and the next two register updates
>> matter?
>>
>> I would have updated the registers immediately and signal the completion
>> later.
>>
>> I am also not sure why it's necessary to mask the done interrupt then
>> unmask it. There is nothing that seems to require this masking?
>>
>> Or are you expecting the code blocked on wait_for_completion to be
>> handled with interrupts masked, which could be quite racy?
> 
> 
> Given how the things turned out in cAVS, some steps are not always
> required. Here, we have very strict implementation and so interrupt are
> masked.
> 
> I'm unsure if relocating complete() to the bottom would bring any
> consequences. And no, the code waiting_for_completion is not expecting
> interrupts to be masked as there is no reply for ROM messages.

it would be just fine to add that the masking is added as an extra
precaution, the order does not matter and the code executed after the
complete() does not assume any masking.

> 
>>> +        ret = IRQ_HANDLED;
>>> +    }
>>> +
>>> +    /* DSP sent new response to process */
>>> +    if (hipc_rsp & spec->hipc_rsp_busy_mask) {
>>> +        /* mask busy interrupt */
>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>> +                      AVS_ADSP_HIPCCTL_BUSY, 0);
>>> +
>>> +        ret = IRQ_WAKE_THREAD;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>
>>> +static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int
>>> timeout)
>>> +{
>>> +    int ret;
>>> +
>>> +again:
>>> +    ret = wait_for_completion_timeout(&ipc->busy_completion,
>>> +                      msecs_to_jiffies(timeout));
>>> +    /*
>>> +     * DSP could be unresponsive at this point e.g. manifested by
>>> +     * EXCEPTION_CAUGHT notification. If so, no point in continuing.
>>
>> EXCEPTION_CAUGHT doesn't seem to be described in this patchset, so not
>> sure what this comment might mean.
> 
> 
> Comment describes the circumstances for the communication failures and
> arrival of EXCEPTION_CAUGHT notification is one of them.

that detail is unnecessary for reviewers.

> 
>>> +     */
>>> +    if (!ipc->ready)
>>> +        return -EPERM;
>>> +
>>> +    if (!ret) {
>>> +        if (!avs_ipc_is_busy(ipc))
>>> +            return -ETIMEDOUT;
>>> +        /*
>>> +         * Firmware did its job, either notification or reply
>>> +         * has been received - now wait until it's processed.
>>> +         */
>>> +        wait_for_completion_killable(&ipc->busy_completion);
>>
>> can you elaborate on why wait_for_completion() is not enough? I haven't
>> seen the 'killable' attribute been used by anyone in sound/
> 
> 
> This is connected to how firmware handles messaging i.e. via queue. you
> may get BUSY interrupt caused by a notification while waiting for the
> reply for your request. Being 'disturbed' by a notification does not
> mean firmware is dead, it's just busy and so we wait until previous
> response is processed entirely.

this does not clarify why 'killable' is necessary?

> 
>>> +    }
>>> +
>>> +    /* Ongoing notification's bottom-half may cause early wakeup */
>>> +    spin_lock(&ipc->rx_lock);
>>> +    if (!ipc->rx_completed) {
>>> +        /* Reply delayed due to notification. */
>>> +        reinit_completion(&ipc->busy_completion);
>>> +        spin_unlock(&ipc->rx_lock);
>>> +        goto again;
>>
>> shouldn't there be some counter to avoid potential infinite loops here?
> 
> 
> This is not a bad idea although the counter is going to be high e.g.:
> 128. With DEBUG-level logs enabled you can get ton of messages before
> your reply gets finally sent.

dev_dbg() in interrupts is usually not very helpful. we're trying to
move to traces instead.

> 
>>> +    }
>>> +
>>> +    spin_unlock(&ipc->rx_lock);
>>> +    return 0;
>>> +}
>>
>>> +static int avs_dsp_do_send_msg(struct avs_dev *adev, struct
>>> avs_ipc_msg *request,
>>> +                   struct avs_ipc_msg *reply, int timeout)
>>> +{
>>> +    struct avs_ipc *ipc = adev->ipc;
>>> +    int ret;
>>> +
>>> +    if (!ipc->ready)
>>> +        return -EPERM;
>>> +
>>> +    mutex_lock(&ipc->msg_mutex);
>>> +
>>> +    spin_lock(&ipc->rx_lock);
>>> +    avs_ipc_msg_init(ipc, reply);
>>> +    avs_dsp_send_tx(adev, request);
>>> +    spin_unlock(&ipc->rx_lock);
>>> +
>>> +    ret = avs_ipc_wait_busy_completion(ipc, timeout);
>>> +    if (ret) {
>>> +        if (ret == -ETIMEDOUT) {
>>> +            dev_crit(adev->dev, "communication severed: %d,
>>> rebooting dsp..\n",
>>> +                 ret);
>>
>> dev_crit() seems over the top if there is a recovery mechanism
> 
> 
> There is just one dev_crit() within entire driver and it's there for a
> reason - communication failure is critical and in practice, should never
> occur in any scenario on the production hardware.

git grep dev_crit shows mostly this being used for temperature and
shorts in codec drivers. that seems more 'critical' than a communication
failure that likely does not result in spontaneous combustion.


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

* Re: [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests
  2022-02-25 18:31     ` Cezary Rojewski
@ 2022-02-25 20:42       ` Pierre-Louis Bossart
  2022-02-28 15:25         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:42 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/25/22 12:31, Cezary Rojewski wrote:
> On 2022-02-25 2:11 AM, Pierre-Louis Bossart wrote:
>> On 2/7/22 06:20, Cezary Rojewski wrote:
>>> A 'Pipeline' represents both a container of module instances, and a
>>> scheduling entity. Multiple pipelines can be bound together to create an
>>> audio graph. The Pipeline state machine is entirely controlled by IPCs
>>> (creation, deletion and state changes).
>>
>> How are the module instances connected within a pipeline? You've said
>> too much or too little here.
> 
> 
> Hmm.. I doubt commit messages is the place to bring up entire FW
> specification. A high level description is provided to give a
> maintainer/reviewer idea of what the pipeline is. Perhaps s/module
> instances/modules/ would suffice.

No one is asking you to to provide the entire specification, just enough
for reviewers with no access to that spec to understand what your intent is.

> 
>>> +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8
>>> priority,
>>> +                u8 instance_id, bool lp, u16 attributes)
>>> +{
>>> +    union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
>>> +    struct avs_ipc_msg request = {0};
>>> +    int ret;
>>> +
>>> +    msg.create_ppl.ppl_mem_size = req_size;
>>> +    msg.create_ppl.ppl_priority = priority;
>>> +    msg.create_ppl.instance_id = instance_id;
>>> +    msg.ext.create_ppl.lp = lp;
>>
>> you may want to describe what the concepts of 'priority', 'lp' and
>> 'attributes' are and which entity defines the values (topology?)
> 
> 
> These fields match firmware equivalents 1:1 and are part of pipeline
> descriptor excepted by firmware when initializing a pipeline. Handlers
> found in messages.c are responsible for one and only one task only:
> sending a concrete message. Part of the driver that implements PCM
> operations (not part of this series) cares about the topology (where
> these values actually come from) and invokes the necessary IPCs.

add a comment then that the driver is just relaying information found in
the topology to the firmware.


>>> +/* Pipeline management messages */
>>> +enum avs_pipeline_state {
>>> +    AVS_PPL_STATE_INVALID,
>>> +    AVS_PPL_STATE_UNINITIALIZED,
>>> +    AVS_PPL_STATE_RESET,
>>> +    AVS_PPL_STATE_PAUSED,
>>> +    AVS_PPL_STATE_RUNNING,
>>> +    AVS_PPL_STATE_EOS,
>>> +    AVS_PPL_STATE_ERROR_STOP,
>>> +    AVS_PPL_STATE_SAVED,
>>> +    AVS_PPL_STATE_RESTORED,
>>
>> can you describe that the last two enums might entail and what the
>> purpose might be?
>>
>> I can see how the firmware state could be saved in IMR for faster
>> suspend/resume, but save/restore at the pipeline level doesn't seem to
>> have an obvious match for an ASoC driver?
> 
> 
> The enum lists all available pipeline states. We're planning to move
> these to uapi later on to allow apps to monitor running pipelines states
> real-time.

That doesn't answer to my question. You are not using the last two in
the rest of this patchset, so why create doubt in the confused brain on
the reviewer?

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

* Re: [PATCH 07/17] ASoC: Intel: avs: Add module management requests
  2022-02-25 18:50     ` Cezary Rojewski
@ 2022-02-25 20:44       ` Pierre-Louis Bossart
  2022-02-28 15:26         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:44 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/25/22 12:50, Cezary Rojewski wrote:
> On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8
>>> instance_id,
>>> +              u8 ppl_id, u8 core_id, u8 domain,
>>
>> you should explain the relationship between ppl_id and core_id. It seems
>> that in the same pipeline different modules instances can be pegged to
>> different cores, which isn't very intuitive given the previous
>> explanation that a pipeline is a scheduling unit.
>>
>> The domain as a u8 is not very clear either, I was under the impression
>> there were only two domains (LL and EDF)?
> 
> 
> Hmm.. such explanations are supposed to be part of HW or FW
> specifications. I don't believe kernel is a place for that. Fields found

how do you expect people with no access to those specs to understand
this code then?

You have to describe the concepts in vague-enough terms that someone
familiar with DSPs can understand.

> here are needed to provide all the necessary information firmware
> expects when requesting INIT_INSTANCE. What's possible and how's
> everything handled internally is for firmware to decide and explain.
> There are no if-statements in the driver's code that force
> ppl_id/core_id relation so I don't see why reader would get an
> impression there is some dependency. What's in the topology gets routed
> to firmware with help of above function.
> 
> Just to confirm: yes, you can have multiple cores engaged in servicing
> modules found in single pipelines.
> 
> In regard to field name/sizes: again, these match firmware equivalents
> 1:1 so it's easy to switch back and forth.

add comments then.

> 
>>> +              void *param, u32 param_size)
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>>> +    struct avs_ipc_msg request;
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    /* firmware expects size provided in dwords */
>>> +    msg.ext.init_instance.param_block_size =
>>> +            DIV_ROUND_UP(param_size, sizeof(u32));
>>> +    msg.ext.init_instance.ppl_instance_id = ppl_id;
>>> +    msg.ext.init_instance.core_id = core_id;
>>> +    msg.ext.init_instance.proc_domain = domain;
>>> +
>>> +    request.header = msg.val;
>>> +    request.data = param;
>>> +    request.size = param_size;
>>
>> isn't there a need to check if the module can be initialized? there's
>> got to be some dependency on pipeline state?
> 
> 
> IPC handlers found in message.c have one and only one purpose only: send
> a message. Firmware will return an error if arguments passed are invalid.
> 
> Also, note that ALSA/ASoC already have a working state machine for
> streaming. There is no reason to re-implement it here.

add a comment then.

> 
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "init instance", ret);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +int avs_ipc_delete_instance(struct avs_dev *adev, u16 module_id, u8
>>> instance_id)
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(DELETE_INSTANCE);
>>> +    struct avs_ipc_msg request = {0};
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    request.header = msg.val;
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "delete instance", ret);
>>> +
>>> +    return ret;
>>
>> same here, can this be used in any pipeline state?
> 
> 
> Ditto.
> 
>>> +}
>>> +
>>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>>> +         u16 dst_module_id, u8 dst_instance_id,
>>> +         u8 dst_queue, u8 src_queue)
>>
>> what does a queue represent?
> 
> 
> In firmware's nomenclature pin/index/queue are synonyms when speaking
> about module instances.

well, that's worthy of a comment...

> 
>>> +{
>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(BIND);
>>> +    struct avs_ipc_msg request = {0};
>>> +    int ret;
>>> +
>>> +    msg.module_id = module_id;
>>> +    msg.instance_id = instance_id;
>>> +    msg.ext.bind_unbind.dst_module_id = dst_module_id;
>>> +    msg.ext.bind_unbind.dst_instance_id = dst_instance_id;
>>> +    msg.ext.bind_unbind.dst_queue = dst_queue;
>>> +    msg.ext.bind_unbind.src_queue = src_queue;
>>> +    request.header = msg.val;
>>> +
>>> +    ret = avs_dsp_send_msg(adev, &request, NULL);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "bind modules", ret);
>>> +
>>> +    return ret;
>>> +}


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

* Re: [PATCH 08/17] ASoC: Intel: avs: Add power management requests
  2022-02-25 19:08     ` Cezary Rojewski
@ 2022-02-25 20:46       ` Pierre-Louis Bossart
  2022-02-28 15:28         ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-25 20:46 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma


> 
>>> +    msg.ext.set_d0ix.wake = enable_pg;
>>
>> simplify the argument? Not sure anyone could understand what wake and
>> enable_pg mean.
> 
> 
> Well, CG and PG are popular shortcuts among Intel audio team and stand
> for clock gating and power gating respectively. 'wake' is firmware
> specific. I can provide a comment, but not all question are going to be
> answered by it. Firmware specification is the place to find the answer
> for most of these.

again please do not assume that anyone reviewing this code has access to
the firmware specification.

> 
>> int avs_ipc_set_d0ix(struct avs_dev *adev, bool wake, bool streaming)
>>
>>> +    msg.ext.set_d0ix.streaming = streaming;
>>> +
>>> +    request.header = msg.val;
>>> +
>>> +    ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
>>> +    if (ret)
>>> +        avs_ipc_err(adev, &request, "set d0ix", ret);
>>> +
>>> +    return ret;
>>> +}
>>> diff --git a/sound/soc/intel/avs/messages.h
>>> b/sound/soc/intel/avs/messages.h
>>> index 1dabd1005327..bbdba4631b1f 100644
>>> --- a/sound/soc/intel/avs/messages.h
>>> +++ b/sound/soc/intel/avs/messages.h
>>> @@ -101,6 +101,8 @@ enum avs_module_msg_type {
>>>       AVS_MOD_LARGE_CONFIG_SET = 4,
>>>       AVS_MOD_BIND = 5,
>>>       AVS_MOD_UNBIND = 6,
>>> +    AVS_MOD_SET_DX = 7,
>>> +    AVS_MOD_SET_D0IX = 8,
>>>       AVS_MOD_DELETE_INSTANCE = 11,
>>>   };
>>>   @@ -137,6 +139,11 @@ union avs_module_msg {
>>>                   u32 dst_queue:3;
>>>                   u32 src_queue:3;
>>>               } bind_unbind;
>>> +            struct {
>>> +                /* cAVS < 2.0 */
>>> +                u32 wake:1;
>>> +                u32 streaming:1;
>>
>> you probably want to explain how a 'streaming' flag is set at the module
>> level? One would think all modules connected in a pipeline would need to
>> use the same flag value.
> 
> 
> Some of the fields are confusing and I agree, but driver has to adhere
> to FW expectations if it wants to be a working one. I would like to
> avoid judging the firmware architecture here, regardless of how
> confusing we think it is.

it's not about judging, just explaining what is expected on the firmware
side and what the driver needs to do.

> 
> 'wake' and 'streaming' fields are part of SET_D0ix message is which part
> of MODULE-type message interface. Base firmware is, from architecture
> point of view, a module of type=0 (module_id) and instance id=0
> (instance_id).
> 
>>> +            } set_d0ix;
>>>           } ext;
>>>       };
>>>   } __packed;
>>> @@ -298,4 +305,13 @@ int avs_ipc_get_large_config(struct avs_dev
>>> *adev, u16 module_id, u8 instance_id
>>>                    u8 param_id, u8 *request_data, size_t request_size,
>>>                    u8 **reply_data, size_t *reply_size);
>>>   +/* DSP cores and domains power management messages */
>>> +struct avs_dxstate_info {
>>> +    u32 core_mask;
>>> +    u32 dx_mask;
>>
>> what is the convention for D0 and D3 in the mask ? which one is 0 or 1?
>>
>> Is this also handled in a hierarchical way where only the bits set in
>> core_mask matter?
> 
> 
> Can provide a short kernel-doc for these two, sure.

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

* Re: [PATCH 03/17] ASoC: Intel: Introduce AVS driver
  2022-02-25 20:23       ` Pierre-Louis Bossart
@ 2022-02-28 14:52         ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 14:52 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:23 PM, Pierre-Louis Bossart wrote:
> 
>>>> +int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
>>>> +{
>>>> +    /* Be permissive to allow for full DSP shutdown in disable path. */
>>>
>>> that comment isn't very clear, what is permissive here?
>>
>>
>> There is no error checking below.
>>
>>>> +    avs_dsp_op(adev, stall, core_mask, true);
>>>> +    avs_dsp_op(adev, reset, core_mask, true);
>>>> +
>>>> +    return avs_dsp_op(adev, power, core_mask, false);
> 
> consider adding a comment then, along the lines of 'we don't prevent
> suspend or shutdown with error checks' or something. 'permissive' was
> rather unclear to me.

Rewording in v2 as suggested.

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

* Re: [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-25 20:37       ` Pierre-Louis Bossart
@ 2022-02-28 15:19         ` Cezary Rojewski
  2022-02-28 15:39           ` Cezary Rojewski
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:19 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:37 PM, Pierre-Louis Bossart wrote:
> 
>>>> +static inline void avs_ipc_err(struct avs_dev *adev, struct
>>>> avs_ipc_msg *tx,
>>>> +                   const char *name, int error)
>>>> +{
>>>> +    /*
>>>> +     * If IPC channel is blocked e.g.: due to ongoing recovery,
>>>> +     * -EPERM error code is expected and thus it's not an actual error.
>>>> +     */
>>>> +    if (error == -EPERM)
>>>> +        dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>>>> +            tx->glb.primary, tx->glb.ext.val, error);
>>>> +    else
>>>> +        dev_err(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name,
>>>> +            tx->glb.primary, tx->glb.ext.val, error);
>>>> +}
>>>
>>> we've used such functions before and the feedback, e.g. from GregKH and
>>> Mark Brown, has consistenly been that this is pushing the use of dev_dbg
>>> too far.
>>
>>
>> In basically all cases the outcome is going to be dev_err(). dev_dbg()
>> is here to help keep DSP-recovery scenario viewer-friendly when checking
>> dmesg. Ideally, there should be no DSP-recoveries to begin with : )
> 
> I will refer you to this thread:
> 
> https://lore.kernel.org/alsa-devel/YGX5AUQi41z52xk8@kroah.com/


That's an interesting lecture, thanks for sharing the link.

Most of the time, we do want to dump an dev_err() if function fails for 
non-trivial reason. During recovery scenario though, we force-disconnect 
all the streams before attempting DSP reboot. That results in "wall of 
red" i.e. error messages. Since we know that all these errors are caused 
by the disconnection of the streams, there is no real value for flagging 
them as errors. It's debug-friendly (for a developer addressing the 
possible problem) to have only important marked as errors in dmesg.

Also, avs_ipc_err() has a very specific purpose and is used only by IPC 
handlers and nowhere else.

>>
>>>> +#define AVS_IPC_TIMEOUT_MS    300
>>>
>>> skl-sst-ipc.c:#define IPC_TIMEOUT_MSECS         3000
>>>
>>> that's one order of magniture lower. please add a comment or align.
>>>
>>>> +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);
>>>> +
>>>> +    ipc->rx.header = header;
>>>> +    if (!msg.status)
>>>> +        memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev),
>>>> +                  ipc->rx.size);
>>>
>>> it wouldn't hurt to describe that the status determines whether
>>> additional information can be read from a mailbox.
>>
>>
>> Isn't that consisted with the behaviour of typical API function? Do not
>> copy memory and return it to the caller if something went wrong along
>> the way?
> 
> oh, I thought this was a case where the header contains all the
> information, and only in specific cases you need to read stuff from the
> mailbox.
> 
> You definitively need to add a comment on whether this is an error
> handling or a feature.


Ack.

>>>> +void avs_dsp_process_response(struct avs_dev *adev, u64 header)
>>>> +{
>>>> +    struct avs_ipc *ipc = adev->ipc;
>>>> +
>>>> +    if (avs_msg_is_reply(header)) {
>>>
>>> the naming is confusing, it's difficult for me to understand that a
>>> 'response' could not be a 'reply'. The two terms are synonyms, aren't
>>> they?
>>
>>
>> Those two are not the same from the firmware's point of view and thus
>> they are not the same here. Response is either a reply or a
>> notification. Replies are solicited, a request has been sent beforehand.
>> Notifications are unsolicited, you are not sure when exactly and if at
>> all they arrive.
> 
> add a comment then.


Ack.

>> Just so I'm not called a heretic later: yes, from English dictionary
>> point of view these two words are synonyms. In general, wording found in
>> this drivers matches firmware equivalents wherever possible to allow
>> developers to switch between these two worlds with minimal adaptation
>> period possible.
> 
>>
>>>> +    /* DSP acked host's request */
>>>> +    if (hipc_ack & spec->hipc_ack_done_mask) {
>>>> +        /* mask done interrupt */
>>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>>> +                      AVS_ADSP_HIPCCTL_DONE, 0);
>>>> +
>>>> +        complete(&ipc->done_completion);
>>>> +
>>>> +        /* tell DSP it has our attention */
>>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ack_offset,
>>>> +                      spec->hipc_ack_done_mask,
>>>> +                      spec->hipc_ack_done_mask);
>>>> +        /* unmask done interrupt */
>>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>>> +                      AVS_ADSP_HIPCCTL_DONE,
>>>> +                      AVS_ADSP_HIPCCTL_DONE);
>>>
>>> does the order between the complete() and the next two register updates
>>> matter?
>>>
>>> I would have updated the registers immediately and signal the completion
>>> later.
>>>
>>> I am also not sure why it's necessary to mask the done interrupt then
>>> unmask it. There is nothing that seems to require this masking?
>>>
>>> Or are you expecting the code blocked on wait_for_completion to be
>>> handled with interrupts masked, which could be quite racy?
>>
>>
>> Given how the things turned out in cAVS, some steps are not always
>> required. Here, we have very strict implementation and so interrupt are
>> masked.
>>
>> I'm unsure if relocating complete() to the bottom would bring any
>> consequences. And no, the code waiting_for_completion is not expecting
>> interrupts to be masked as there is no reply for ROM messages.
> 
> it would be just fine to add that the masking is added as an extra
> precaution, the order does not matter and the code executed after the
> complete() does not assume any masking.


Ack.

>>
>>>> +        ret = IRQ_HANDLED;
>>>> +    }
>>>> +
>>>> +    /* DSP sent new response to process */
>>>> +    if (hipc_rsp & spec->hipc_rsp_busy_mask) {
>>>> +        /* mask busy interrupt */
>>>> +        snd_hdac_adsp_updatel(adev, spec->hipc_ctl_offset,
>>>> +                      AVS_ADSP_HIPCCTL_BUSY, 0);
>>>> +
>>>> +        ret = IRQ_WAKE_THREAD;
>>>> +    }
>>>> +
>>>> +    return ret;
>>>> +}
>>>
>>>> +static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int
>>>> timeout)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +again:
>>>> +    ret = wait_for_completion_timeout(&ipc->busy_completion,
>>>> +                      msecs_to_jiffies(timeout));
>>>> +    /*
>>>> +     * DSP could be unresponsive at this point e.g. manifested by
>>>> +     * EXCEPTION_CAUGHT notification. If so, no point in continuing.
>>>
>>> EXCEPTION_CAUGHT doesn't seem to be described in this patchset, so not
>>> sure what this comment might mean.
>>
>>
>> Comment describes the circumstances for the communication failures and
>> arrival of EXCEPTION_CAUGHT notification is one of them.
> 
> that detail is unnecessary for reviewers.


Ack.

>>
>>>> +     */
>>>> +    if (!ipc->ready)
>>>> +        return -EPERM;
>>>> +
>>>> +    if (!ret) {
>>>> +        if (!avs_ipc_is_busy(ipc))
>>>> +            return -ETIMEDOUT;
>>>> +        /*
>>>> +         * Firmware did its job, either notification or reply
>>>> +         * has been received - now wait until it's processed.
>>>> +         */
>>>> +        wait_for_completion_killable(&ipc->busy_completion);
>>>
>>> can you elaborate on why wait_for_completion() is not enough? I haven't
>>> seen the 'killable' attribute been used by anyone in sound/
>>
>>
>> This is connected to how firmware handles messaging i.e. via queue. you
>> may get BUSY interrupt caused by a notification while waiting for the
>> reply for your request. Being 'disturbed' by a notification does not
>> mean firmware is dead, it's just busy and so we wait until previous
>> response is processed entirely.
> 
> this does not clarify why 'killable' is necessary?


Usage of 'killable' variant adheres to its documentation. Sys calls can 
terminate the waiter. More user friendly.

>>
>>>> +    }
>>>> +
>>>> +    /* Ongoing notification's bottom-half may cause early wakeup */
>>>> +    spin_lock(&ipc->rx_lock);
>>>> +    if (!ipc->rx_completed) {
>>>> +        /* Reply delayed due to notification. */
>>>> +        reinit_completion(&ipc->busy_completion);
>>>> +        spin_unlock(&ipc->rx_lock);
>>>> +        goto again;
>>>
>>> shouldn't there be some counter to avoid potential infinite loops here?
>>
>>
>> This is not a bad idea although the counter is going to be high e.g.:
>> 128. With DEBUG-level logs enabled you can get ton of messages before
>> your reply gets finally sent.
> 
> dev_dbg() in interrupts is usually not very helpful. we're trying to
> move to traces instead.


Wasn't precise enough, I appologize for that. By "DEBUG-level logs" I 
meant firmware logging, not dev_dbg() on kernel side. When enabled with 
log level DEBUG, you will get at least 1 message per sys tick, resulting 
in gigabyte logs in no time.

>>
>>>> +    }
>>>> +
>>>> +    spin_unlock(&ipc->rx_lock);
>>>> +    return 0;
>>>> +}
>>>
>>>> +static int avs_dsp_do_send_msg(struct avs_dev *adev, struct
>>>> avs_ipc_msg *request,
>>>> +                   struct avs_ipc_msg *reply, int timeout)
>>>> +{
>>>> +    struct avs_ipc *ipc = adev->ipc;
>>>> +    int ret;
>>>> +
>>>> +    if (!ipc->ready)
>>>> +        return -EPERM;
>>>> +
>>>> +    mutex_lock(&ipc->msg_mutex);
>>>> +
>>>> +    spin_lock(&ipc->rx_lock);
>>>> +    avs_ipc_msg_init(ipc, reply);
>>>> +    avs_dsp_send_tx(adev, request);
>>>> +    spin_unlock(&ipc->rx_lock);
>>>> +
>>>> +    ret = avs_ipc_wait_busy_completion(ipc, timeout);
>>>> +    if (ret) {
>>>> +        if (ret == -ETIMEDOUT) {
>>>> +            dev_crit(adev->dev, "communication severed: %d,
>>>> rebooting dsp..\n",
>>>> +                 ret);
>>>
>>> dev_crit() seems over the top if there is a recovery mechanism
>>
>>
>> There is just one dev_crit() within entire driver and it's there for a
>> reason - communication failure is critical and in practice, should never
>> occur in any scenario on the production hardware.
> 
> git grep dev_crit shows mostly this being used for temperature and
> shorts in codec drivers. that seems more 'critical' than a communication
> failure that likely does not result in spontaneous combustion.


Few dev_crit()s can also be found in other components as well.

Without audio and graphics there is no real 'user experience'. Abrupt 
closure of communication between DSP firmware and kernel driver can, and 
usually is a consequence of either an undefined behaviour (in process 
running on DSP) or hardware issue. While I can't spare the details here 
for obvious reasons, not all situations can even be recovered with 
reboot. That usually depends in which power wells registers reside. The 
100% confirmed solution for laptops is removing battery for a second or 
day to force G3.

Considering this, I believe having a single dev_crit() here is justified.


Regards,
Czarek

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

* Re: [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests
  2022-02-25 20:42       ` Pierre-Louis Bossart
@ 2022-02-28 15:25         ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:25 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:42 PM, Pierre-Louis Bossart wrote:
> On 2/25/22 12:31, Cezary Rojewski wrote:
>> On 2022-02-25 2:11 AM, Pierre-Louis Bossart wrote:
>>> On 2/7/22 06:20, Cezary Rojewski wrote:
>>>> A 'Pipeline' represents both a container of module instances, and a
>>>> scheduling entity. Multiple pipelines can be bound together to create an
>>>> audio graph. The Pipeline state machine is entirely controlled by IPCs
>>>> (creation, deletion and state changes).
>>>
>>> How are the module instances connected within a pipeline? You've said
>>> too much or too little here.
>>
>>
>> Hmm.. I doubt commit messages is the place to bring up entire FW
>> specification. A high level description is provided to give a
>> maintainer/reviewer idea of what the pipeline is. Perhaps s/module
>> instances/modules/ would suffice.
> 
> No one is asking you to to provide the entire specification, just enough
> for reviewers with no access to that spec to understand what your intent is.


Removed the confusing part of the message in v2.

>>
>>>> +int avs_ipc_create_pipeline(struct avs_dev *adev, u16 req_size, u8
>>>> priority,
>>>> +                u8 instance_id, bool lp, u16 attributes)
>>>> +{
>>>> +    union avs_global_msg msg = AVS_GLOBAL_REQUEST(CREATE_PIPELINE);
>>>> +    struct avs_ipc_msg request = {0};
>>>> +    int ret;
>>>> +
>>>> +    msg.create_ppl.ppl_mem_size = req_size;
>>>> +    msg.create_ppl.ppl_priority = priority;
>>>> +    msg.create_ppl.instance_id = instance_id;
>>>> +    msg.ext.create_ppl.lp = lp;
>>>
>>> you may want to describe what the concepts of 'priority', 'lp' and
>>> 'attributes' are and which entity defines the values (topology?)
>>
>>
>> These fields match firmware equivalents 1:1 and are part of pipeline
>> descriptor excepted by firmware when initializing a pipeline. Handlers
>> found in messages.c are responsible for one and only one task only:
>> sending a concrete message. Part of the driver that implements PCM
>> operations (not part of this series) cares about the topology (where
>> these values actually come from) and invokes the necessary IPCs.
> 
> add a comment then that the driver is just relaying information found in
> the topology to the firmware.


Ack.

>>>> +/* Pipeline management messages */
>>>> +enum avs_pipeline_state {
>>>> +    AVS_PPL_STATE_INVALID,
>>>> +    AVS_PPL_STATE_UNINITIALIZED,
>>>> +    AVS_PPL_STATE_RESET,
>>>> +    AVS_PPL_STATE_PAUSED,
>>>> +    AVS_PPL_STATE_RUNNING,
>>>> +    AVS_PPL_STATE_EOS,
>>>> +    AVS_PPL_STATE_ERROR_STOP,
>>>> +    AVS_PPL_STATE_SAVED,
>>>> +    AVS_PPL_STATE_RESTORED,
>>>
>>> can you describe that the last two enums might entail and what the
>>> purpose might be?
>>>
>>> I can see how the firmware state could be saved in IMR for faster
>>> suspend/resume, but save/restore at the pipeline level doesn't seem to
>>> have an obvious match for an ASoC driver?
>>
>>
>> The enum lists all available pipeline states. We're planning to move
>> these to uapi later on to allow apps to monitor running pipelines states
>> real-time.
> 
> That doesn't answer to my question. You are not using the last two in
> the rest of this patchset, so why create doubt in the confused brain on
> the reviewer?


Removed both in v2, thanks.

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

* Re: [PATCH 07/17] ASoC: Intel: avs: Add module management requests
  2022-02-25 20:44       ` Pierre-Louis Bossart
@ 2022-02-28 15:26         ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:26 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:44 PM, Pierre-Louis Bossart wrote:
> On 2/25/22 12:50, Cezary Rojewski wrote:
>> On 2022-02-25 2:27 AM, Pierre-Louis Bossart wrote:
>>>> +int avs_ipc_init_instance(struct avs_dev *adev, u16 module_id, u8
>>>> instance_id,
>>>> +              u8 ppl_id, u8 core_id, u8 domain,
>>>
>>> you should explain the relationship between ppl_id and core_id. It seems
>>> that in the same pipeline different modules instances can be pegged to
>>> different cores, which isn't very intuitive given the previous
>>> explanation that a pipeline is a scheduling unit.
>>>
>>> The domain as a u8 is not very clear either, I was under the impression
>>> there were only two domains (LL and EDF)?
>>
>>
>> Hmm.. such explanations are supposed to be part of HW or FW
>> specifications. I don't believe kernel is a place for that. Fields found
> 
> how do you expect people with no access to those specs to understand
> this code then?
> 
> You have to describe the concepts in vague-enough terms that someone
> familiar with DSPs can understand.


Added kernel-doc for said function to address this.

>> here are needed to provide all the necessary information firmware
>> expects when requesting INIT_INSTANCE. What's possible and how's
>> everything handled internally is for firmware to decide and explain.
>> There are no if-statements in the driver's code that force
>> ppl_id/core_id relation so I don't see why reader would get an
>> impression there is some dependency. What's in the topology gets routed
>> to firmware with help of above function.
>>
>> Just to confirm: yes, you can have multiple cores engaged in servicing
>> modules found in single pipelines.
>>
>> In regard to field name/sizes: again, these match firmware equivalents
>> 1:1 so it's easy to switch back and forth.
> 
> add comments then.


Ack.

>>
>>>> +              void *param, u32 param_size)
>>>> +{
>>>> +    union avs_module_msg msg = AVS_MODULE_REQUEST(INIT_INSTANCE);
>>>> +    struct avs_ipc_msg request;
>>>> +    int ret;
>>>> +
>>>> +    msg.module_id = module_id;
>>>> +    msg.instance_id = instance_id;
>>>> +    /* firmware expects size provided in dwords */
>>>> +    msg.ext.init_instance.param_block_size =
>>>> +            DIV_ROUND_UP(param_size, sizeof(u32));
>>>> +    msg.ext.init_instance.ppl_instance_id = ppl_id;
>>>> +    msg.ext.init_instance.core_id = core_id;
>>>> +    msg.ext.init_instance.proc_domain = domain;
>>>> +
>>>> +    request.header = msg.val;
>>>> +    request.data = param;
>>>> +    request.size = param_size;
>>>
>>> isn't there a need to check if the module can be initialized? there's
>>> got to be some dependency on pipeline state?
>>
>>
>> IPC handlers found in message.c have one and only one purpose only: send
>> a message. Firmware will return an error if arguments passed are invalid.
>>
>> Also, note that ALSA/ASoC already have a working state machine for
>> streaming. There is no reason to re-implement it here.
> 
> add a comment then.


Ack.

>>>> +}
>>>> +
>>>> +int avs_ipc_bind(struct avs_dev *adev, u16 module_id, u8 instance_id,
>>>> +         u16 dst_module_id, u8 dst_instance_id,
>>>> +         u8 dst_queue, u8 src_queue)
>>>
>>> what does a queue represent?
>>
>>
>> In firmware's nomenclature pin/index/queue are synonyms when speaking
>> about module instances.
> 
> well, that's worthy of a comment...


Ack.

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

* Re: [PATCH 08/17] ASoC: Intel: avs: Add power management requests
  2022-02-25 20:46       ` Pierre-Louis Bossart
@ 2022-02-28 15:28         ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:28 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:46 PM, Pierre-Louis Bossart wrote:
> 
>>
>>>> +    msg.ext.set_d0ix.wake = enable_pg;
>>>
>>> simplify the argument? Not sure anyone could understand what wake and
>>> enable_pg mean.
>>
>>
>> Well, CG and PG are popular shortcuts among Intel audio team and stand
>> for clock gating and power gating respectively. 'wake' is firmware
>> specific. I can provide a comment, but not all question are going to be
>> answered by it. Firmware specification is the place to find the answer
>> for most of these.
> 
> again please do not assume that anyone reviewing this code has access to
> the firmware specification.


Provided simple description for the SET_D0IX message.

>>
>>> int avs_ipc_set_d0ix(struct avs_dev *adev, bool wake, bool streaming)
>>>
>>>> +    msg.ext.set_d0ix.streaming = streaming;
>>>> +
>>>> +    request.header = msg.val;
>>>> +
>>>> +    ret = avs_dsp_send_pm_msg(adev, &request, NULL, false);
>>>> +    if (ret)
>>>> +        avs_ipc_err(adev, &request, "set d0ix", ret);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> diff --git a/sound/soc/intel/avs/messages.h
>>>> b/sound/soc/intel/avs/messages.h
>>>> index 1dabd1005327..bbdba4631b1f 100644
>>>> --- a/sound/soc/intel/avs/messages.h
>>>> +++ b/sound/soc/intel/avs/messages.h
>>>> @@ -101,6 +101,8 @@ enum avs_module_msg_type {
>>>>        AVS_MOD_LARGE_CONFIG_SET = 4,
>>>>        AVS_MOD_BIND = 5,
>>>>        AVS_MOD_UNBIND = 6,
>>>> +    AVS_MOD_SET_DX = 7,
>>>> +    AVS_MOD_SET_D0IX = 8,
>>>>        AVS_MOD_DELETE_INSTANCE = 11,
>>>>    };
>>>>    @@ -137,6 +139,11 @@ union avs_module_msg {
>>>>                    u32 dst_queue:3;
>>>>                    u32 src_queue:3;
>>>>                } bind_unbind;
>>>> +            struct {
>>>> +                /* cAVS < 2.0 */
>>>> +                u32 wake:1;
>>>> +                u32 streaming:1;
>>>
>>> you probably want to explain how a 'streaming' flag is set at the module
>>> level? One would think all modules connected in a pipeline would need to
>>> use the same flag value.
>>
>>
>> Some of the fields are confusing and I agree, but driver has to adhere
>> to FW expectations if it wants to be a working one. I would like to
>> avoid judging the firmware architecture here, regardless of how
>> confusing we think it is.
> 
> it's not about judging, just explaining what is expected on the firmware
> side and what the driver needs to do.


Dropped all the "cavs < 2.0" unclear comments.

>>
>> 'wake' and 'streaming' fields are part of SET_D0ix message is which part
>> of MODULE-type message interface. Base firmware is, from architecture
>> point of view, a module of type=0 (module_id) and instance id=0
>> (instance_id).
>>
>>>> +            } set_d0ix;
>>>>            } ext;
>>>>        };
>>>>    } __packed;
>>>> @@ -298,4 +305,13 @@ int avs_ipc_get_large_config(struct avs_dev
>>>> *adev, u16 module_id, u8 instance_id
>>>>                     u8 param_id, u8 *request_data, size_t request_size,
>>>>                     u8 **reply_data, size_t *reply_size);
>>>>    +/* DSP cores and domains power management messages */
>>>> +struct avs_dxstate_info {
>>>> +    u32 core_mask;
>>>> +    u32 dx_mask;
>>>
>>> what is the convention for D0 and D3 in the mask ? which one is 0 or 1?
>>>
>>> Is this also handled in a hierarchical way where only the bits set in
>>> core_mask matter?
>>
>>
>> Can provide a short kernel-doc for these two, sure.


Provided a short comments instead.

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

* Re: [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-25 20:21       ` Pierre-Louis Bossart
@ 2022-02-28 15:30         ` Cezary Rojewski
  2022-02-28 17:20           ` Pierre-Louis Bossart
  0 siblings, 1 reply; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:30 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-25 9:21 PM, Pierre-Louis Bossart wrote:
>>>> +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
>>>> +{
>>>> +    u32 mask;
>>>> +    int ret;
>>>> +
>>>> +    ret = avs_dsp_core_enable(adev, core_mask);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    mask = core_mask & ~AVS_MAIN_CORE_MASK;
>>>
>>> so here BIT(MAIN_CORE) is zero in mask
>>
>>
>> What's wrong with AVS_MAIN_CORE_MASK being used explicitly?
>>
>>>> +    if (!mask)
>>>> +        /*
>>>> +         * without main core, fw is dead anyway
>>>> +         * so setting D0 for it is futile.
>>>
>>> I don't get the comment, you explicitly discarded the main core with
>>> your logical AND above, so this test means that all other non-main cores
>>> are disabled.
>>
>> There is no setting D0 for MAIN_CORE as firmware is not operational
>> without it. Firmware needs to be notified about D3 -> D0 transitions
>> only in case of non-MAIN_COREs.
> 
> the comment was about 'without main core'.
> 
> This is difficult to follow, because you've discarded the main code in
> the if (!mask), so that's an always-true case, which makes the rest of
> the explanations not so clear.


I'm afraid, not seeing the problem. It's clearly not always-true 
statement as 'mask' can be non-zero after discarding the MAIN_CORE.

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

* Re: [PATCH 04/17] ASoC: Intel: avs: Inter process communication
  2022-02-28 15:19         ` Cezary Rojewski
@ 2022-02-28 15:39           ` Cezary Rojewski
  0 siblings, 0 replies; 61+ messages in thread
From: Cezary Rojewski @ 2022-02-28 15:39 UTC (permalink / raw)
  To: Pierre-Louis Bossart, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma

On 2022-02-28 4:19 PM, Cezary Rojewski wrote:

> Few dev_crit()s can also be found in other components as well.
> 
> Without audio and graphics there is no real 'user experience'. Abrupt 
> closure of communication between DSP firmware and kernel driver can, and 
> usually is a consequence of either an undefined behaviour (in process 
> running on DSP) or hardware issue. While I can't spare the details here 
> for obvious reasons, not all situations can even be recovered with 
> reboot. That usually depends in which power wells registers reside. The 
> 100% confirmed solution for laptops is removing battery for a second or 
> day to force G3.


s/second or day/second or two/

Hope people are not panicking after that typo =)

> Considering this, I believe having a single dev_crit() here is justified.

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

* Re: [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management
  2022-02-28 15:30         ` Cezary Rojewski
@ 2022-02-28 17:20           ` Pierre-Louis Bossart
  0 siblings, 0 replies; 61+ messages in thread
From: Pierre-Louis Bossart @ 2022-02-28 17:20 UTC (permalink / raw)
  To: Cezary Rojewski, alsa-devel
  Cc: upstream, harshapriya.n, rad, tiwai, hdegoede, broonie,
	amadeuszx.slawinski, cujomalainey, lma



On 2/28/22 09:30, Cezary Rojewski wrote:
> On 2022-02-25 9:21 PM, Pierre-Louis Bossart wrote:
>>>>> +static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
>>>>> +{
>>>>> +    u32 mask;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = avs_dsp_core_enable(adev, core_mask);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    mask = core_mask & ~AVS_MAIN_CORE_MASK;
>>>>
>>>> so here BIT(MAIN_CORE) is zero in mask
>>>
>>>
>>> What's wrong with AVS_MAIN_CORE_MASK being used explicitly?
>>>
>>>>> +    if (!mask)
>>>>> +        /*
>>>>> +         * without main core, fw is dead anyway
>>>>> +         * so setting D0 for it is futile.
>>>>
>>>> I don't get the comment, you explicitly discarded the main core with
>>>> your logical AND above, so this test means that all other non-main
>>>> cores
>>>> are disabled.
>>>
>>> There is no setting D0 for MAIN_CORE as firmware is not operational
>>> without it. Firmware needs to be notified about D3 -> D0 transitions
>>> only in case of non-MAIN_COREs.
>>
>> the comment was about 'without main core'.
>>
>> This is difficult to follow, because you've discarded the main code in
>> the if (!mask), so that's an always-true case, which makes the rest of
>> the explanations not so clear.
> 
> 
> I'm afraid, not seeing the problem. It's clearly not always-true
> statement as 'mask' can be non-zero after discarding the MAIN_CORE.

mask = core_mask & ~AVS_MAIN_CORE_MASK;

-> main core is ignored.

comment says "without main core, fw is dead anyway"

since you ignored the main core, is fw dead in all cases?

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

end of thread, other threads:[~2022-02-28 18:06 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-07 12:20 [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
2022-02-07 12:20 ` [PATCH 01/17] ALSA: hda: Add helper macros for DSP capable devices Cezary Rojewski
2022-02-07 12:20 ` [PATCH 02/17] ASoC: Export DAI register and widget ctor and dctor functions Cezary Rojewski
2022-02-07 12:20 ` [PATCH 03/17] ASoC: Intel: Introduce AVS driver Cezary Rojewski
2022-02-24 23:55   ` Pierre-Louis Bossart
2022-02-25 16:56     ` Cezary Rojewski
2022-02-25 20:23       ` Pierre-Louis Bossart
2022-02-28 14:52         ` Cezary Rojewski
2022-02-07 12:20 ` [PATCH 04/17] ASoC: Intel: avs: Inter process communication Cezary Rojewski
2022-02-25  0:56   ` Pierre-Louis Bossart
2022-02-25 18:06     ` Cezary Rojewski
2022-02-25 20:37       ` Pierre-Louis Bossart
2022-02-28 15:19         ` Cezary Rojewski
2022-02-28 15:39           ` Cezary Rojewski
2022-02-07 12:20 ` [PATCH 05/17] ASoC: Intel: avs: Add code loading requests Cezary Rojewski
2022-02-25  1:02   ` Pierre-Louis Bossart
2022-02-25 18:08     ` Cezary Rojewski
2022-02-07 12:20 ` [PATCH 06/17] ASoC: Intel: avs: Add pipeline management requests Cezary Rojewski
2022-02-25  1:11   ` Pierre-Louis Bossart
2022-02-25 18:31     ` Cezary Rojewski
2022-02-25 20:42       ` Pierre-Louis Bossart
2022-02-28 15:25         ` Cezary Rojewski
2022-02-07 12:20 ` [PATCH 07/17] ASoC: Intel: avs: Add module " Cezary Rojewski
2022-02-25  1:27   ` Pierre-Louis Bossart
2022-02-25 18:50     ` Cezary Rojewski
2022-02-25 20:44       ` Pierre-Louis Bossart
2022-02-28 15:26         ` Cezary Rojewski
2022-02-07 12:20 ` [PATCH 08/17] ASoC: Intel: avs: Add power " Cezary Rojewski
2022-02-25  1:37   ` Pierre-Louis Bossart
2022-02-25 19:08     ` Cezary Rojewski
2022-02-25 20:46       ` Pierre-Louis Bossart
2022-02-28 15:28         ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 09/17] ASoC: Intel: avs: Add ROM requests Cezary Rojewski
2022-02-25  1:42   ` Pierre-Louis Bossart
2022-02-25 19:19     ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 10/17] ASoC: Intel: avs: Add basefw runtime-parameter requests Cezary Rojewski
2022-02-07 12:21 ` [PATCH 11/17] ASoC: Intel: avs: Firmware resources management utilities Cezary Rojewski
2022-02-25  1:53   ` Pierre-Louis Bossart
2022-02-25 19:20     ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 12/17] ASoC: Intel: avs: Declare module configuration types Cezary Rojewski
2022-02-07 12:21 ` [PATCH 13/17] ASoC: Intel: avs: Dynamic firmware resources management Cezary Rojewski
2022-02-25  2:02   ` Pierre-Louis Bossart
2022-02-25 19:27     ` Cezary Rojewski
2022-02-25 20:21       ` Pierre-Louis Bossart
2022-02-28 15:30         ` Cezary Rojewski
2022-02-28 17:20           ` Pierre-Louis Bossart
2022-02-07 12:21 ` [PATCH 14/17] ASoC: Intel: avs: General code loading flow Cezary Rojewski
2022-02-25  2:15   ` Pierre-Louis Bossart
2022-02-25 19:37     ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 15/17] ASoC: Intel: avs: Implement CLDMA transfer Cezary Rojewski
2022-02-25  2:18   ` Pierre-Louis Bossart
2022-02-25 19:38     ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 16/17] ASoC: Intel: avs: Code loading over CLDMA Cezary Rojewski
2022-02-25  2:21   ` Pierre-Louis Bossart
2022-02-25 19:38     ` Cezary Rojewski
2022-02-07 12:21 ` [PATCH 17/17] ASoC: Intel: avs: Code loading over HDA Cezary Rojewski
2022-02-21 11:51 ` [PATCH 00/17] ASoC: Intel: AVS - Audio DSP for cAVS Cezary Rojewski
2022-02-25  2:35 ` Pierre-Louis Bossart
2022-02-25 15:44   ` Cezary Rojewski
2022-02-25 16:33     ` Pierre-Louis Bossart
2022-02-25 18:07   ` Mark Brown

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.