[PATCH 00/18] ASoC: SOF: Intel: hda-mlink: HDaudio multi-link extension update
Hi,
The following series adds the core support to handle the recently updated HDaudio multi-link support to hanlde non HDA links, like SoundWire/DMIC/SSP on Intel platform.
For details, please see the first patch which documents the current mlink support (introduced at Skylake) and the new extensions, arriving with LNL.
There is no change in functionality for existing HDA support, the extension is backwards compatible with existing implementations.
Regards, Peter --- Pierre-Louis Bossart (18): Documentation: sound: add description of Intel HDaudio multi-links ALSA: hda: add HDaudio Extended link definitions ASoC: SOF: Intel: hda-mlink: improve hda_bus_ml_free() helper ASoC: SOF: Intel: hda-mlink: add return value for hda_bus_ml_get_capabilities() ASoC: SOF: Intel: hda-mlink: move to a dedicated module ASoC: SOF: Intel: hda-mlink: add structures to parse ALT links ASoC: SOF: Intel: hda-mlink: special-case HDaudio regular links ASoC: SOF: Intel: hda-mlink: introduce helpers for 'extended links' PM ASoC: SOF: Intel: hda-mlink: add convenience helpers for SoundWire PM ASoC: SOF: Intel: hda-mlink: add helper to return sublink count ASoC: SOF: Intel: hda-mlink: add helpers to enable/check interrupts ASoC: SOF: Intel: hda-mlink: add helpers to set link SYNC frequency ASoC: SOF: Intel: hda-mlink: add helpers for sync_arm/sync_go ASoC: SOF: Intel: hda-mlink: add helper to check cmdsync ASoC: SOF: Intel: hda-mlink: program SoundWire LSDIID registers ASoC: SOF: Intel: hda-mlink: add helpers to retrieve DMIC/SSP hlink ASoC: SOF: Intel: hda-mlink: add helper to offload link ownership ASoC: SOF: Intel: hda-mlink: add helper to retrieve eml_lock
Documentation/sound/hd-audio/index.rst | 1 + .../sound/hd-audio/intel-multi-link.rst | 312 +++++++ include/sound/hda-mlink.h | 166 ++++ include/sound/hda_register.h | 40 +- sound/soc/sof/intel/Kconfig | 7 + sound/soc/sof/intel/Makefile | 5 +- sound/soc/sof/intel/hda-ctrl.c | 1 + sound/soc/sof/intel/hda-dsp.c | 1 + sound/soc/sof/intel/hda-mlink.c | 822 +++++++++++++++++- sound/soc/sof/intel/hda.c | 2 + sound/soc/sof/intel/hda.h | 20 - 11 files changed, 1331 insertions(+), 46 deletions(-) create mode 100644 Documentation/sound/hd-audio/intel-multi-link.rst create mode 100644 include/sound/hda-mlink.h
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Add description of 'Skylake' multi-link structure added in 2015 and recent extensions to support SoundWire/DMIC/SSP interfaces.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- Documentation/sound/hd-audio/index.rst | 1 + .../sound/hd-audio/intel-multi-link.rst | 312 ++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 Documentation/sound/hd-audio/intel-multi-link.rst
diff --git a/Documentation/sound/hd-audio/index.rst b/Documentation/sound/hd-audio/index.rst index 6e12de9fc34e..baefe4a5d165 100644 --- a/Documentation/sound/hd-audio/index.rst +++ b/Documentation/sound/hd-audio/index.rst @@ -9,3 +9,4 @@ HD-Audio controls dp-mst realtek-pc-beep + intel-multi-link diff --git a/Documentation/sound/hd-audio/intel-multi-link.rst b/Documentation/sound/hd-audio/intel-multi-link.rst new file mode 100644 index 000000000000..bf0bb78833e7 --- /dev/null +++ b/Documentation/sound/hd-audio/intel-multi-link.rst @@ -0,0 +1,312 @@ +.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +.. include:: <isonum.txt> + +================================================ +HDAudio multi-link extensions on Intel platforms +================================================ + +:Copyright: |copy| 2023 Intel Corporation + +This file documents the 'multi-link structure' introduced in 2015 with +the Skylake processor and recently extended in newer Intel platforms + +HDaudio existing link mapping (2015 addition in SkyLake) +======================================================== + +External HDAudio codecs are handled with link #0, while iDISP codec +for HDMI/DisplayPort is handled with link #1. + +The only change to the 2015 definitions is the declaration of the +LCAP.ALT=0x0 - since the ALT bit was previously reserved, this is a +backwards-compatible change. + +LCTL.SPA and LCTL.CPA are automatically set when exiting reset. They +are only used in existing drivers when the SCF value needs to be +corrected. + +Basic structure for HDaudio codecs +---------------------------------- + +:: + + +-----------+ + | ML cap #0 | + +-----------+ + | ML cap #1 |---+ + +-----------+ | + | + +--> 0x0 +---------------+ LCAP + | ALT=0 | + +---------------+ + | S192 | + +---------------+ + | S96 | + +---------------+ + | S48 | + +---------------+ + | S24 | + +---------------+ + | S12 | + +---------------+ + | S6 | + +---------------+ + + 0x4 +---------------+ LCTL + | INTSTS | + +---------------+ + | CPA | + +---------------+ + | SPA | + +---------------+ + | SCF | + +---------------+ + + 0x8 +---------------+ LOSIDV + | L1OSIVD15 | + +---------------+ + | L1OSIDV.. | + +---------------+ + | L1OSIDV1 | + +---------------+ + + 0xC +---------------+ LSDIID + | SDIID14 | + +---------------+ + | SDIID... | + +---------------+ + | SDIID0 | + +---------------+ + +SoundWire HDaudio extended link mapping +======================================= + +A SoundWire extended link is identified when LCAP.ALT=1 and +LEPTR.ID=0. + +DMA control uses the existing LOSIDV register. + +Changes include additional descriptions for enumeration that were not +present in earlier generations. + +- multi-link synchronization: capabilities in LCAP.LSS and control in LSYNC +- number of sublinks (manager IP) in LCAP.LSCOUNT +- power management moved from SHIM to LCTL.SPA bits +- hand-over to the DSP for access to multi-link registers, SHIM/IP with LCTL.OFLEN +- mapping of SoundWire codecs to SDI ID bits +- move of SHIM and Cadence registers to different offsets, with no + change in functionality. The LEPTR.PTR value is an offset from the + ML address, with a default value of 0x30000. + +Extended structure for SoundWire (assuming 4 Manager IP) +-------------------------------------------------------- + +:: + + +-----------+ + | ML cap #0 | + +-----------+ + | ML cap #1 | + +-----------+ + | ML cap #2 |---+ + +-----------+ | + | + +--> 0x0 +---------------+ LCAP + | ALT=1 | + +---------------+ + | INTC | + +---------------+ + | OFLS | + +---------------+ + | LSS | + +---------------+ + | SLCOUNT=4 |-----------+ + +---------------+ | + | + 0x4 +---------------+ LCTL | + | INTSTS | | + +---------------+ | + | CPA (x bits) | | + +---------------+ | + | SPA (x bits) | | + +---------------+ for each sublink x + | INTEN | | + +---------------+ | + | OFLEN | | + +---------------+ | + | + 0x8 +---------------+ LOSIDV | + | L1OSIVD15 | | + +---------------+ | + | L1OSIDV.. | | + +---------------+ | + | L1OSIDV1 | +---+----------------------------------------------------------+ + +---------------+ | | + v | + 0xC + 0x2 * x +---------------+ LSDIIDx +---> 0x30000 +-----------------+ 0x00030000 | + | SDIID14 | | | SoundWire SHIM | | + +---------------+ | | generic | | + | SDIID... | | +-----------------+ 0x00030100 | + +---------------+ | | SoundWire IP | | + | SDIID0 | | +-----------------+ 0x00036000 | + +---------------+ | | SoundWire SHIM | | + | | vendor-specific | | + 0x1C +---------------+ LSYNC | +-----------------+ | + | CMDSYNC | | v + +---------------+ | +-----------------+ 0x00030000 + 0x8000 * x + | SYNCGO | | | SoundWire SHIM | + +---------------+ | | generic | + | SYNCPU | | +-----------------+ 0x00030100 + 0x8000 * x + +---------------+ | | SoundWire IP | + | SYNPRD | | +-----------------+ 0x00036000 + 0x8000 * x + +---------------+ | | SoundWire SHIM | + | | vendor-specific | + 0x20 +---------------+ LEPTR | +-----------------+ + | ID = 0 | | + +---------------+ | + | VER | | + +---------------+ | + | PTR |------------+ + +---------------+ + + +DMIC HDaudio extended link mapping +================================== + +A DMIC extended link is identified when LCAP.ALT=1 and +LEPTR.ID=0xC1 are set. + +DMA control uses the existing LOSIDV register + +Changes include additional descriptions for enumeration that were not +present in earlier generations. + +- multi-link synchronization: capabilities in LCAP.LSS and control in LSYNC +- power management with LCTL.SPA bits +- hand-over to the DSP for access to multi-link registers, SHIM/IP with LCTL.OFLEN + +- move of DMIC registers to different offsets, with no change in + functionality. The LEPTR.PTR value is an offset from the ML + address, with a default value of 0x10000. + +Extended structure for DMIC +--------------------------- + +:: + + +-----------+ + | ML cap #0 | + +-----------+ + | ML cap #1 | + +-----------+ + | ML cap #2 |---+ + +-----------+ | + | + +--> 0x0 +---------------+ LCAP + | ALT=1 | + +---------------+ + | INTC | + +---------------+ + | OFLS | + +---------------+ + | SLCOUNT=1 | + +---------------+ + + 0x4 +---------------+ LCTL + | INTSTS | + +---------------+ + | CPA | + +---------------+ + | SPA | + +---------------+ + | INTEN | + +---------------+ + | OFLEN | + +---------------+ +---> 0x10000 +-----------------+ 0x00010000 + | | DMIC SHIM | + 0x8 +---------------+ LOSIDV | | generic | + | L1OSIVD15 | | +-----------------+ 0x00010100 + +---------------+ | | DMIC IP | + | L1OSIDV.. | | +-----------------+ 0x00016000 + +---------------+ | | DMIC SHIM | + | L1OSIDV1 | | | vendor-specific | + +---------------+ | +-----------------+ + | + 0x20 +---------------+ LEPTR | + | ID = 0xC1 | | + +---------------+ | + | VER | | + +---------------+ | + | PTR |-----------+ + +---------------+ + + +SSP HDaudio extended link mapping +================================= + +A DMIC extended link is identified when LCAP.ALT=1 and +LEPTR.ID=0xC0 are set. + +DMA control uses the existing LOSIDV register + +Changes include additional descriptions for enumeration and control that were not +present in earlier generations: +- number of sublinks (SSP IP instances) in LCAP.LSCOUNT +- power management moved from SHIM to LCTL.SPA bits +- hand-over to the DSP for access to multi-link registers, SHIM/IP +with LCTL.OFLEN +- move of SHIM and SSP IP registers to different offsets, with no +change in functionality. The LEPTR.PTR value is an offset from the ML +address, with a default value of 0x28000. + +Extended structure for SSP (assuming 3 instances of the IP) +----------------------------------------------------------- + +:: + + +-----------+ + | ML cap #0 | + +-----------+ + | ML cap #1 | + +-----------+ + | ML cap #2 |---+ + +-----------+ | + | + +--> 0x0 +---------------+ LCAP + | ALT=1 | + +---------------+ + | INTC | + +---------------+ + | OFLS | + +---------------+ + | SLCOUNT=3 |-------------------------for each sublink x -------------------------+ + +---------------+ | + | + 0x4 +---------------+ LCTL | + | INTSTS | | + +---------------+ | + | CPA (x bits) | | + +---------------+ | + | SPA (x bits) | | + +---------------+ | + | INTEN | | + +---------------+ | + | OFLEN | | + +---------------+ +---> 0x28000 +-----------------+ 0x00028000 | + | | SSP SHIM | | + 0x8 +---------------+ LOSIDV | | generic | | + | L1OSIVD15 | | +-----------------+ 0x00028100 | + +---------------+ | | SSP IP | | + | L1OSIDV.. | | +-----------------+ 0x00028C00 | + +---------------+ | | SSP SHIM | | + | L1OSIDV1 | | | vendor-specific | | + +---------------+ | +-----------------+ | + | v + 0x20 +---------------+ LEPTR | +-----------------+ 0x00028000 + 0x1000 * x + | ID = 0xC0 | | | SSP SHIM | + +---------------+ | | generic | + | VER | | +-----------------+ 0x00028100 + 0x1000 * x + +---------------+ | | SSP IP | + | PTR |-----------+ +-----------------+ 0x00028C00 + 0x1000 * x + +---------------+ | SSP SHIM | + | vendor-specific | + +-----------------+
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Add new definitions for the HDaudio Extended link support, specifically new registers for SoundWire, Intel DMIC and INTEL SSP interfaces.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda_register.h | 40 ++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index d37cf43546eb..9c7872c0ca79 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -258,14 +258,27 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_ML_BASE 0x40 #define AZX_ML_INTERVAL 0x40
+/* HDaudio registers valid for HDaudio and HDaudio extended links */ #define AZX_REG_ML_LCAP 0x00 -#define AZX_REG_ML_LCTL 0x04
+#define AZX_ML_HDA_LCAP_ALT BIT(28) +#define AZX_ML_HDA_LCAP_ALT_HDA 0x0 +#define AZX_ML_HDA_LCAP_ALT_HDA_EXT 0x1 + +#define AZX_ML_HDA_LCAP_INTC BIT(27) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_OFLS BIT(26) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_LSS BIT(23) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_SLCOUNT GENMASK(22, 20) /* only used if ALT == 1 */ + +#define AZX_REG_ML_LCTL 0x04 +#define AZX_ML_LCTL_INTSTS BIT(31) /* only used if ALT == 1 */ #define AZX_ML_LCTL_CPA BIT(23) #define AZX_ML_LCTL_CPA_SHIFT 23 #define AZX_ML_LCTL_SPA BIT(16) #define AZX_ML_LCTL_SPA_SHIFT 16 -#define AZX_ML_LCTL_SCF GENMASK(3, 0) +#define AZX_ML_LCTL_INTEN BIT(5) /* only used if ALT == 1 */ +#define AZX_ML_LCTL_OFLEN BIT(4) /* only used if ALT == 1 */ +#define AZX_ML_LCTL_SCF GENMASK(3, 0) /* only used if ALT == 0 */
#define AZX_REG_ML_LOSIDV 0x08
@@ -273,12 +286,35 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_ML_LOSIDV_STREAM_MASK 0xFFFE
#define AZX_REG_ML_LSDIID 0x0C +#define AZX_REG_ML_LSDIID_OFFSET(x) (0x0C + (x) * 0x02) /* only used if ALT == 1 */ + +/* HDaudio registers only valid if LCAP.ALT == 0 */ #define AZX_REG_ML_LPSOO 0x10 #define AZX_REG_ML_LPSIO 0x12 #define AZX_REG_ML_LWALFC 0x18 #define AZX_REG_ML_LOUTPAY 0x20 #define AZX_REG_ML_LINPAY 0x30
+/* HDaudio Extended link registers only valid if LCAP.ALT == 1 */ +#define AZX_REG_ML_LSYNC 0x1C + +#define AZX_REG_ML_LSYNC_CMDSYNC BIT(24) +#define AZX_REG_ML_LSYNC_CMDSYNC_SHIFT 24 +#define AZX_REG_ML_LSYNC_SYNCGO BIT(23) +#define AZX_REG_ML_LSYNC_SYNCPU BIT(20) +#define AZX_REG_ML_LSYNC_SYNCPRD GENMASK(19, 0) + +#define AZX_REG_ML_LEPTR 0x20 + +#define AZX_REG_ML_LEPTR_ID GENMASK(31, 24) +#define AZX_REG_ML_LEPTR_ID_SHIFT 24 +#define AZX_REG_ML_LEPTR_ID_SDW 0x00 +#define AZX_REG_ML_LEPTR_ID_INTEL_SSP 0xC0 +#define AZX_REG_ML_LEPTR_ID_INTEL_DMIC 0xC1 +#define AZX_REG_ML_LEPTR_ID_INTEL_UAOL 0xC2 +#define AZX_REG_ML_LEPTR_VER GENMASK(23, 20) +#define AZX_REG_ML_LEPTR_PTR GENMASK(19, 0) + /* registers for DMA Resume Capability Structure */ #define AZX_DRSM_CAP_ID 0x5 #define AZX_REG_DRSM_CTL 0x4
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Use list_for_each_entry_safe() instead of open-coding.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- sound/soc/sof/intel/hda-mlink.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 76ab9a2e7bb3..ac9bf477f413 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -36,13 +36,12 @@ void hda_bus_ml_get_capabilities(struct hdac_bus *bus)
void hda_bus_ml_free(struct hdac_bus *bus) { - struct hdac_ext_link *hlink; + struct hdac_ext_link *hlink, *_h;
if (!bus->mlcap) return;
- while (!list_empty(&bus->hlink_list)) { - hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list); + list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { list_del(&hlink->list); kfree(hlink); }
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Add return value - this will need additional work in the caller.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- sound/soc/sof/intel/hda-mlink.c | 5 +++-- sound/soc/sof/intel/hda.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index ac9bf477f413..e426d5e41e52 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -28,10 +28,11 @@
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus) +int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { if (bus->mlcap) - snd_hdac_ext_bus_get_ml_capabilities(bus); + return snd_hdac_ext_bus_get_ml_capabilities(bus); + return 0; }
void hda_bus_ml_free(struct hdac_bus *bus) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2a43bfc14583..259b34eea677 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -765,7 +765,7 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus); +int hda_bus_ml_get_capabilities(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); @@ -774,7 +774,7 @@ int hda_bus_ml_suspend(struct hdac_bus *bus);
#else
-static inline void hda_bus_ml_get_capabilities(struct hdac_bus *bus) { } +static inline int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } static inline void hda_bus_ml_free(struct hdac_bus *bus) { } static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Some of the functions will be used for SoundWire enumeration and power management, to avoid cycles in module dependencies and simplify integration all the HDaudio multi-link needs to move to a dedicated module.
Drop no longer needed headers at the same time.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 31 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/Kconfig | 7 +++++++ sound/soc/sof/intel/Makefile | 5 ++++- sound/soc/sof/intel/hda-ctrl.c | 1 + sound/soc/sof/intel/hda-dsp.c | 1 + sound/soc/sof/intel/hda-mlink.c | 24 +++++++++++------------- sound/soc/sof/intel/hda.c | 2 ++ sound/soc/sof/intel/hda.h | 20 -------------------- 8 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 include/sound/hda-mlink.h
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h new file mode 100644 index 000000000000..beef5f509e47 --- /dev/null +++ b/include/sound/hda-mlink.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022-2023 Intel Corporation. All rights reserved. + */ + +struct hdac_bus; + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) + +int hda_bus_ml_get_capabilities(struct hdac_bus *bus); +void hda_bus_ml_free(struct hdac_bus *bus); +void hda_bus_ml_put_all(struct hdac_bus *bus); +void hda_bus_ml_reset_losidv(struct hdac_bus *bus); +int hda_bus_ml_resume(struct hdac_bus *bus); +int hda_bus_ml_suspend(struct hdac_bus *bus); + +#else + +static inline int +hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } + +static inline void hda_bus_ml_free(struct hdac_bus *bus) { } +static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } +static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } +static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } +static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } + +#endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 715ba8a7f2f8..f4eeacf1f281 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -269,6 +269,13 @@ config SND_SOC_SOF_HDA_COMMON select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE select SND_SOC_SOF_HDA_PROBES + select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + +config SND_SOC_SOF_HDA_MLINK + tristate help This option is not user-selectable but automagically handled by 'select' statements at a higher level. diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 38ab86b6a9fe..fdb463c12e91 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -5,10 +5,12 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ - hda-dai.o hda-dai-ops.o hda-bus.o hda-mlink.o \ + hda-dai.o hda-dai-ops.o hda-bus.o \ skl.o hda-loader-skl.o \ apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o
+snd-sof-intel-hda-mlink-objs := hda-mlink.o + snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
snd-sof-intel-hda-objs := hda-codec.o @@ -19,6 +21,7 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o +obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
snd-sof-pci-intel-tng-objs := pci-tng.o diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index e1dba6b79065..84bf01bd360a 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -19,6 +19,7 @@ #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> #include <sound/hda_component.h> +#include <sound/hda-mlink.h> #include "../ops.h" #include "hda.h"
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index c9231aeacc53..a8722f0774b1 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include <sound/hda-mlink.h> #include <trace/events/sof_intel.h> #include "../sof-audio.h" #include "../ops.h" diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index e426d5e41e52..fbf86f2350fb 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -12,21 +12,11 @@
#include <sound/hdaudio_ext.h> #include <sound/hda_register.h> +#include <sound/hda-mlink.h>
-#include <linux/acpi.h> #include <linux/module.h> -#include <linux/soundwire/sdw.h> -#include <linux/soundwire/sdw_intel.h> -#include <sound/intel-dsp-config.h> -#include <sound/intel-nhlt.h> -#include <sound/sof.h> -#include <sound/sof/xtensa.h> -#include "../sof-audio.h" -#include "../sof-pci-dev.h" -#include "../ops.h" -#include "hda.h" - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { @@ -34,6 +24,7 @@ int hda_bus_ml_get_capabilities(struct hdac_bus *bus) return snd_hdac_ext_bus_get_ml_capabilities(bus); return 0; } +EXPORT_SYMBOL_NS(hda_bus_ml_get_capabilities, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_free(struct hdac_bus *bus) { @@ -47,6 +38,7 @@ void hda_bus_ml_free(struct hdac_bus *bus) kfree(hlink); } } +EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_put_all(struct hdac_bus *bus) { @@ -55,6 +47,7 @@ void hda_bus_ml_put_all(struct hdac_bus *bus) list_for_each_entry(hlink, &bus->hlink_list, list) snd_hdac_ext_bus_link_put(bus, hlink); } +EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { @@ -64,6 +57,7 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) list_for_each_entry(hlink, &bus->hlink_list, list) writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); } +EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_resume(struct hdac_bus *bus) { @@ -80,10 +74,14 @@ int hda_bus_ml_resume(struct hdac_bus *bus) } return 0; } +EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_suspend(struct hdac_bus *bus) { return snd_hdac_ext_bus_link_power_down_all(bus); } +EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
#endif + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 65389c7278e2..ac61233029b7 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -26,6 +26,7 @@ #include <sound/intel-nhlt.h> #include <sound/sof.h> #include <sound/sof/xtensa.h> +#include <sound/hda-mlink.h> #include "../sof-audio.h" #include "../sof-pci-dev.h" #include "../ops.h" @@ -1647,3 +1648,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI); MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); MODULE_IMPORT_NS(SOUNDWIRE_INTEL); +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 259b34eea677..0e0cfa81a8f2 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -763,26 +763,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - -int hda_bus_ml_get_capabilities(struct hdac_bus *bus); -void hda_bus_ml_free(struct hdac_bus *bus); -void hda_bus_ml_put_all(struct hdac_bus *bus); -void hda_bus_ml_reset_losidv(struct hdac_bus *bus); -int hda_bus_ml_resume(struct hdac_bus *bus); -int hda_bus_ml_suspend(struct hdac_bus *bus); - -#else - -static inline int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } -static inline void hda_bus_ml_free(struct hdac_bus *bus) { } -static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } -static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } -static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } -static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } - -#endif /* CONFIG_SND_SOC_SOF_HDA */ - /* * Trace Control. */
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Extend hdac_ext_link to store information needed for ALT links. Follow-up patches will include more functional patches for power-up and down.
Note that this patch suggests the use of an 'eml_lock' to serialize access to shared registers. SoundWire-specific sequence require the lock to be taken at a higher level, as a result the helpers added in follow-up patches will provide 'unlocked' versions when needed.
Also note that the low-level sequences with the 'hdaml_' prefix are taken directly from the hardware specifications - naming conventions included. The code will be split in two, with locking and linked-list management handled separately to avoid mixing required hardware setup and Linux-based resource management.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- sound/soc/sof/intel/hda-mlink.c | 217 +++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index fbf86f2350fb..cc944311a671 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -14,14 +14,221 @@ #include <sound/hda_register.h> #include <sound/hda-mlink.h>
+#include <linux/bitfield.h> #include <linux/module.h>
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
+/** + * struct hdac_ext2_link - HDAudio extended+alternate link + * + * @hext_link: hdac_ext_link + * @alt: flag set for alternate extended links + * @intc: boolean for interrupt capable + * @ofls: boolean for offload support + * @lss: boolean for link synchronization capabilities + * @slcount: sublink count + * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines) + * @elver: extended link version + * @leptr: extended link pointer + * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits + * in LCTL register + * @base_ptr: pointer to shim/ip/shim_vs space + * @instance_offset: offset between each of @slcount instances managed by link + * @shim_offset: offset to SHIM register base + * @ip_offset: offset to IP register base + * @shim_vs_offset: offset to vendor-specific (VS) SHIM base + */ +struct hdac_ext2_link { + struct hdac_ext_link hext_link; + + /* read directly from LCAP register */ + bool alt; + bool intc; + bool ofls; + bool lss; + int slcount; + int elid; + int elver; + u32 leptr; + + struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */ + + /* internal values computed from LCAP contents */ + void __iomem *base_ptr; + u32 instance_offset; + u32 shim_offset; + u32 ip_offset; + u32 shim_vs_offset; +}; + +#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link) + +#define AZX_REG_SDW_INSTANCE_OFFSET 0x8000 +#define AZX_REG_SDW_SHIM_OFFSET 0x0 +#define AZX_REG_SDW_IP_OFFSET 0x100 +#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000 + +/* only one instance supported */ +#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100 +#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000 + +#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000 +#define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_SSP_IP_OFFSET 0x100 +#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00 + +/* only one instance supported */ +#define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100 +#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00 + +/* HDAML section - this part follows sequences in the hardware specification, + * including naming conventions and the use of the hdaml_ prefix. + * The code is intentionally minimal with limited dependencies on frameworks or + * helpers. Locking and scanning lists is handled at a higher level + */ + +static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link, + void __iomem *ml_addr, int link_idx) +{ + struct hdac_ext_link *hlink = &h2link->hext_link; + u32 base_offset; + + hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP); + + h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps); + + /* handle alternate extensions */ + if (!h2link->alt) { + h2link->slcount = 1; + + /* + * LSDIID is initialized by hardware for HDaudio link, + * it needs to be setup by software for alternate links + */ + hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID); + + dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n", + link_idx, hlink->lsdiid); + + return 0; + } + + h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps); + h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps); + h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps); + + /* read slcount (increment due to zero-based hardware representation */ + h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1; + dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n", + link_idx, h2link->slcount); + + /* find IP ID and offsets */ + h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR); + + h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr); + + base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr); + h2link->base_ptr = hlink->ml_addr + base_offset; + + switch (h2link->elid) { + case AZX_REG_ML_LEPTR_ID_SDW: + h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_SDW_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_DMIC: + h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_SSP: + h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: + h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + default: + dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n", + link_idx, h2link->elid); + return -EINVAL; + } + return 0; +} + +/* END HDAML section */ + +static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret; + + h2link = kzalloc(sizeof(*h2link), GFP_KERNEL); + if (!h2link) + return -ENOMEM; + + /* basic initialization */ + hlink = &h2link->hext_link; + + hlink->index = index; + hlink->bus = bus; + hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index); + + ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index); + if (ret < 0) { + kfree(h2link); + return ret; + } + + mutex_init(&h2link->eml_lock); + + list_add_tail(&hlink->list, &bus->hlink_list); + + /* + * HDaudio regular links are powered-on by default, the + * refcount needs to be initialized. + */ + if (!h2link->alt) + hlink->ref_count = 1; + + return 0; +} + int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { - if (bus->mlcap) - return snd_hdac_ext_bus_get_ml_capabilities(bus); + u32 link_count; + int ret; + int i; + + if (!bus->mlcap) + return 0; + + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; + + dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); + + for (i = 0; i < link_count; i++) { + ret = hda_ml_alloc_h2link(bus, i); + if (ret < 0) { + hda_bus_ml_free(bus); + return ret; + } + } return 0; } EXPORT_SYMBOL_NS(hda_bus_ml_get_capabilities, SND_SOC_SOF_HDA_MLINK); @@ -29,13 +236,17 @@ EXPORT_SYMBOL_NS(hda_bus_ml_get_capabilities, SND_SOC_SOF_HDA_MLINK); void hda_bus_ml_free(struct hdac_bus *bus) { struct hdac_ext_link *hlink, *_h; + struct hdac_ext2_link *h2link;
if (!bus->mlcap) return;
list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { list_del(&hlink->list); - kfree(hlink); + h2link = hdac_ext_link_to_ext2(hlink); + + mutex_destroy(&h2link->eml_lock); + kfree(h2link); } } EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
On Mon, 27 Mar 2023 13:29:19 +0200, Peter Ujfalusi wrote:
int hda_bus_ml_get_capabilities(struct hdac_bus *bus) {
- if (bus->mlcap)
return snd_hdac_ext_bus_get_ml_capabilities(bus);
- u32 link_count;
- int ret;
- int i;
- if (!bus->mlcap)
return 0;
- link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
- dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
- for (i = 0; i < link_count; i++) {
ret = hda_ml_alloc_h2link(bus, i);
if (ret < 0) {
hda_bus_ml_free(bus);
return ret;
}
- } return 0;
This makes that each call of hda_bus_ml_get_capabilities() adds the h2link entries blindly. If the driver calls it multiple times mistakenly (the function name sounds as if it's just a helper to query the capability bits), it'll lead to doubly entries. Maybe adding some check would be safer, IMO.
thanks,
Takashi
On 3/30/23 10:51, Takashi Iwai wrote:
On Mon, 27 Mar 2023 13:29:19 +0200, Peter Ujfalusi wrote:
int hda_bus_ml_get_capabilities(struct hdac_bus *bus) {
- if (bus->mlcap)
return snd_hdac_ext_bus_get_ml_capabilities(bus);
- u32 link_count;
- int ret;
- int i;
- if (!bus->mlcap)
return 0;
- link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
- dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
- for (i = 0; i < link_count; i++) {
ret = hda_ml_alloc_h2link(bus, i);
if (ret < 0) {
hda_bus_ml_free(bus);
return ret;
}
- } return 0;
This makes that each call of hda_bus_ml_get_capabilities() adds the h2link entries blindly. If the driver calls it multiple times mistakenly (the function name sounds as if it's just a helper to query the capability bits), it'll lead to doubly entries. Maybe adding some check would be safer, IMO.
Interesting comment, I didn't think of that one.
This function is currently called in the probe and indirectly via hda_init_caps(). I think the driver framework guarantees the probe is only called once, doesn't it?
we can also rename this function to make it clearer if there are any suggestions, but the name is aligned with previous implementations of the snd_hdac_ext stuff.
On Thu, 30 Mar 2023 18:09:36 +0200, Pierre-Louis Bossart wrote:
On 3/30/23 10:51, Takashi Iwai wrote:
On Mon, 27 Mar 2023 13:29:19 +0200, Peter Ujfalusi wrote:
int hda_bus_ml_get_capabilities(struct hdac_bus *bus) {
- if (bus->mlcap)
return snd_hdac_ext_bus_get_ml_capabilities(bus);
- u32 link_count;
- int ret;
- int i;
- if (!bus->mlcap)
return 0;
- link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
- dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
- for (i = 0; i < link_count; i++) {
ret = hda_ml_alloc_h2link(bus, i);
if (ret < 0) {
hda_bus_ml_free(bus);
return ret;
}
- } return 0;
This makes that each call of hda_bus_ml_get_capabilities() adds the h2link entries blindly. If the driver calls it multiple times mistakenly (the function name sounds as if it's just a helper to query the capability bits), it'll lead to doubly entries. Maybe adding some check would be safer, IMO.
Interesting comment, I didn't think of that one.
This function is currently called in the probe and indirectly via hda_init_caps(). I think the driver framework guarantees the probe is only called once, doesn't it?
we can also rename this function to make it clearer if there are any suggestions, but the name is aligned with previous implementations of the snd_hdac_ext stuff.
Yeah, naming it as an init function would avoid the misuse.
Takashi
int hda_bus_ml_get_capabilities(struct hdac_bus *bus) {
- if (bus->mlcap)
return snd_hdac_ext_bus_get_ml_capabilities(bus);
- u32 link_count;
- int ret;
- int i;
- if (!bus->mlcap)
return 0;
- link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
- dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
- for (i = 0; i < link_count; i++) {
ret = hda_ml_alloc_h2link(bus, i);
if (ret < 0) {
hda_bus_ml_free(bus);
return ret;
}
- } return 0;
This makes that each call of hda_bus_ml_get_capabilities() adds the h2link entries blindly. If the driver calls it multiple times mistakenly (the function name sounds as if it's just a helper to query the capability bits), it'll lead to doubly entries. Maybe adding some check would be safer, IMO.
Interesting comment, I didn't think of that one.
This function is currently called in the probe and indirectly via hda_init_caps(). I think the driver framework guarantees the probe is only called once, doesn't it?
we can also rename this function to make it clearer if there are any suggestions, but the name is aligned with previous implementations of the snd_hdac_ext stuff.
Yeah, naming it as an init function would avoid the misuse.
Sure thing, I'll rename it as hda_bus_ml_init().
Thanks for the feedback.
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
For backwards compatibility, HDaudio/iDISP links are powered-on when exiting reset, and the existing driver forces them to be powered-off when entering S0ix. In addition, the get/put helpers are invoked directly by the ASoC codec drivers, which a historical layering violation.
Extended links are powered-on by software only, during the probe and DAI startup phases. This calls for a different handling of the 'regular' and 'extended' audio links.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- sound/soc/sof/intel/hda-mlink.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index cc944311a671..90b68ae2564c 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -255,8 +255,12 @@ void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink;
- list_for_each_entry(hlink, &bus->hlink_list, list) - snd_hdac_ext_bus_link_put(bus, hlink); + list_for_each_entry(hlink, &bus->hlink_list, list) { + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt) + snd_hdac_ext_bus_link_put(bus, hlink); + } } EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
@@ -277,7 +281,9 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
/* power up links that were active before suspend */ list_for_each_entry(hlink, &bus->hlink_list, list) { - if (hlink->ref_count) { + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt && hlink->ref_count) { ret = snd_hdac_ext_bus_link_power_up(hlink); if (ret < 0) return ret; @@ -289,7 +295,19 @@ EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_suspend(struct hdac_bus *bus) { - return snd_hdac_ext_bus_link_power_down_all(bus); + struct hdac_ext_link *hlink; + int ret; + + list_for_each_entry(hlink, &bus->hlink_list, list) { + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt) { + ret = snd_hdac_ext_bus_link_power_down(hlink); + if (ret < 0) + return ret; + } + } + return 0; } EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Add helpers to program SPA/CPA bits, using a mutex to access the shared LCTL register if required.
All links are managed with the same LCTLx.SPA bits. However there are quite a few implementation details to be aware of:
Legacy HDaudio multi-links are powered-up when exiting reset, which requires the ref_count to be manually set to one when initializing the link.
Alternate links for SoundWire/DMIC/SSP need to be explicitly powered-up before accessing the SHIM/IP/Vendor-Specific SHIM space for each sublink. DMIC/SSP/SoundWire are all different cases with a different device/dai/hlink relationship.
SoundWire will handle power management with the auxiliary device resume/suspend routine. The ref_count is not necessary in this case.
The DMIC/SSP will by contrast handle the power management from DAI .startup and .shutdown callbacks.
The SSP has a 1:1 mapping between sublink and DAI, but it's bidirectional so the ref_count will help avoid turning off the sublink when one of the two directions is still in use.
The DMIC has a single link but two DAIs for data generated at different sampling frequencies, again the ref_count will make sure the two DAIs can be used concurrently.
And last the SoundWire Intel require power-up/down and bank switch to be handled with a lock already taken, so the 'eml_lock' is made optional with the _unlocked versions of the helpers.
Note that the _check_power_active() implementation is similar to previous helpers in sound/hda/ext, with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with .e.g. readl_poll_timeout()
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 32 +++++++ sound/soc/sof/intel/hda-mlink.c | 163 ++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index beef5f509e47..6908af849dd5 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -12,6 +12,13 @@ struct hdac_bus;
int hda_bus_ml_get_capabilities(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); + +int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); +int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); + +int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink); +int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -23,6 +30,31 @@ static inline int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; }
static inline void hda_bus_ml_free(struct hdac_bus *bus) { } + +static inline int +hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 90b68ae2564c..4cfef4007d0c 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -170,6 +170,68 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link, return 0; }
+/* + * Hardware recommendations are to wait ~10us before checking any hardware transition + * reported by bits changing status. + * This value does not need to be super-precise, a slack of 5us is perfectly acceptable. + * The worst-case is about 1ms before reporting an issue + */ +#define HDAML_POLL_DELAY_MIN_US 10 +#define HDAML_POLL_DELAY_SLACK_US 5 +#define HDAML_POLL_DELAY_RETRY 100 + +static int check_power_active(u32 __iomem *lctl, int sublink, bool enable) +{ + int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT; + int retry = HDAML_POLL_DELAY_RETRY; + u32 val; + + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + do { + val = readl(lctl); + if (enable) { + if (val & mask) + return 0; + } else { + if (!(val & mask)) + return 0; + } + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + + } while (--retry); + + return -EIO; +} + +static int hdaml_link_init(u32 __iomem *lctl, int sublink) +{ + u32 val; + u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT; + + val = readl(lctl); + val |= mask; + + writel(val, lctl); + + return check_power_active(lctl, sublink, true); +} + +static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink) +{ + u32 val; + u32 mask; + + val = readl(lctl); + mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT; + val &= ~mask; + + writel(val, lctl); + + return check_power_active(lctl, sublink, false); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -251,6 +313,107 @@ void hda_bus_ml_free(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
+static struct hdac_ext2_link * +find_ext2_link(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext_link *hlink; + + list_for_each_entry(hlink, &bus->hlink_list, list) { + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (h2link->alt == alt && h2link->elid == elid) + return h2link; + } + + return NULL; +} + +static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, + bool eml_lock) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret = 0; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (sublink >= h2link->slcount) + return -EINVAL; + + hlink = &h2link->hext_link; + + if (eml_lock) + mutex_lock(&h2link->eml_lock); + + if (++hlink->ref_count > 1) + goto skip_init; + + ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); + +skip_init: + if (eml_lock) + mutex_unlock(&h2link->eml_lock); + + return ret; +} + +int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); + +static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, + bool eml_lock) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret = 0; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (sublink >= h2link->slcount) + return -EINVAL; + + hlink = &h2link->hext_link; + + if (eml_lock) + mutex_lock(&h2link->eml_lock); + + if (--hlink->ref_count > 0) + goto skip_shutdown; + + ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); + +skip_shutdown: + if (eml_lock) + mutex_unlock(&h2link->eml_lock); + + return ret; +} + +int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink;
On 3/27/2023 1:29 PM, Peter Ujfalusi wrote:
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Add helpers to program SPA/CPA bits, using a mutex to access the shared LCTL register if required.
All links are managed with the same LCTLx.SPA bits. However there are quite a few implementation details to be aware of:
Legacy HDaudio multi-links are powered-up when exiting reset, which requires the ref_count to be manually set to one when initializing the link.
Alternate links for SoundWire/DMIC/SSP need to be explicitly powered-up before accessing the SHIM/IP/Vendor-Specific SHIM space for each sublink. DMIC/SSP/SoundWire are all different cases with a different device/dai/hlink relationship.
SoundWire will handle power management with the auxiliary device resume/suspend routine. The ref_count is not necessary in this case.
The DMIC/SSP will by contrast handle the power management from DAI .startup and .shutdown callbacks.
The SSP has a 1:1 mapping between sublink and DAI, but it's bidirectional so the ref_count will help avoid turning off the sublink when one of the two directions is still in use.
The DMIC has a single link but two DAIs for data generated at different sampling frequencies, again the ref_count will make sure the two DAIs can be used concurrently.
And last the SoundWire Intel require power-up/down and bank switch to be handled with a lock already taken, so the 'eml_lock' is made optional with the _unlocked versions of the helpers.
Note that the _check_power_active() implementation is similar to previous helpers in sound/hda/ext, with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with .e.g. readl_poll_timeout()
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com
include/sound/hda-mlink.h | 32 +++++++ sound/soc/sof/intel/hda-mlink.c | 163 ++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+)
...
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 90b68ae2564c..4cfef4007d0c 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -170,6 +170,68 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link, return 0; }
+/*
- Hardware recommendations are to wait ~10us before checking any hardware transition
- reported by bits changing status.
- This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
- The worst-case is about 1ms before reporting an issue
- */
+#define HDAML_POLL_DELAY_MIN_US 10 +#define HDAML_POLL_DELAY_SLACK_US 5 +#define HDAML_POLL_DELAY_RETRY 100
+static int check_power_active(u32 __iomem *lctl, int sublink, bool enable)
Should last argument be named 'active' instead of 'enable'? It would make more sense to me.
+{
- int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
- int retry = HDAML_POLL_DELAY_RETRY;
- u32 val;
- usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- do {
val = readl(lctl);
if (enable) {
if (val & mask)
return 0;
} else {
if (!(val & mask))
return 0;
}
usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- } while (--retry);
- return -EIO;
+}
...
+static int check_power_active(u32 __iomem *lctl, int sublink, bool enable)
Should last argument be named 'active' instead of 'enable'? It would make more sense to me.
Naming is the hardest part, eh?
I am not super happy with 'active', since the 'not-active' part is not very clear: it's not suspended, it's really powered-off/disabled.
I also didn't want to introduce a power state, since again it's on or off and we don't want to introduce the Dx concepts here.
If I had to revisit this, my preference would be:
static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
On 3/28/2023 3:24 PM, Pierre-Louis Bossart wrote:
+static int check_power_active(u32 __iomem *lctl, int sublink, bool enable)
Should last argument be named 'active' instead of 'enable'? It would make more sense to me.
Naming is the hardest part, eh?
I don't disagree ;)
I am not super happy with 'active', since the 'not-active' part is not very clear: it's not suspended, it's really powered-off/disabled.
I also didn't want to introduce a power state, since again it's on or off and we don't want to introduce the Dx concepts here.
If I had to revisit this, my preference would be:
static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
Yes that reads better to me.
On Mon, 27 Mar 2023 13:29:21 +0200, Peter Ujfalusi wrote:
+/*
- Hardware recommendations are to wait ~10us before checking any hardware transition
- reported by bits changing status.
- This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
- The worst-case is about 1ms before reporting an issue
- */
+#define HDAML_POLL_DELAY_MIN_US 10 +#define HDAML_POLL_DELAY_SLACK_US 5 +#define HDAML_POLL_DELAY_RETRY 100
+static int check_power_active(u32 __iomem *lctl, int sublink, bool enable) +{
- int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
- int retry = HDAML_POLL_DELAY_RETRY;
- u32 val;
- usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- do {
val = readl(lctl);
if (enable) {
if (val & mask)
return 0;
} else {
if (!(val & mask))
return 0;
}
usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- } while (--retry);
- return -EIO;
+}
Can read_poll_timeout() and co be alternative?
thanks,
Takashi
+/*
- Hardware recommendations are to wait ~10us before checking any hardware transition
- reported by bits changing status.
- This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
- The worst-case is about 1ms before reporting an issue
- */
+#define HDAML_POLL_DELAY_MIN_US 10 +#define HDAML_POLL_DELAY_SLACK_US 5 +#define HDAML_POLL_DELAY_RETRY 100
+static int check_power_active(u32 __iomem *lctl, int sublink, bool enable) +{
- int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
- int retry = HDAML_POLL_DELAY_RETRY;
- u32 val;
- usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- do {
val = readl(lctl);
if (enable) {
if (val & mask)
return 0;
} else {
if (!(val & mask))
return 0;
}
usleep_range(HDAML_POLL_DELAY_MIN_US,
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
- } while (--retry);
- return -EIO;
+}
Can read_poll_timeout() and co be alternative?
Yes they can. I chose the simple route because I find it clearer, and because we modified the sleep/retries compared to previous implementations.
here's what I wrote in the commit message:
" Note that the _check_power_active() implementation is similar to previous helpers in sound/hda/ext, with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with .e.g. readl_poll_timeout() "
If you want to jump directly to the next step that'd be fine. Peter had the same comment, so I'll go with the flow.
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
The updated SoundWire Intel driver will need to rely on Extended HDaudio links for power management, but it doesn't need to be aware of all the HDaudio structures. Add convenience helpers to avoid polluting SoundWire drivers too much with HDaudio information.
Since the SoundWire/Intel solution already takes the lock at a higher level, the _unlocked PM helpers are used.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 9 +++++++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 21 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 6908af849dd5..74737032a632 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -19,6 +19,9 @@ int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink); +int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -55,6 +58,12 @@ hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int s return 0; }
+static inline int +hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) { return 0; } + +static inline int +hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; } + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 4cfef4007d0c..310bb4c6822e 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -414,6 +414,18 @@ int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, i } EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) +{ + return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) +{ + return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink;
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
This is needed for SoundWire integration.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 17 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 74737032a632..b8f889fa46c4 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -13,6 +13,8 @@ struct hdac_bus; int hda_bus_ml_get_capabilities(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus);
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
@@ -34,6 +36,9 @@ hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; }
static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
+static inline int +hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 310bb4c6822e..93d532acd1c2 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -328,6 +328,18 @@ find_ext2_link(struct hdac_bus *bus, bool alt, int elid) return NULL; }
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + return h2link->slcount; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
When INTC is set, LCTL exposes INTEN and INTSTS fields.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 8 +++++ sound/soc/sof/intel/hda-mlink.c | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index b8f889fa46c4..ebc7c5b07557 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -14,6 +14,8 @@ int hda_bus_ml_get_capabilities(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus);
int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); +void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable); +bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -39,6 +41,12 @@ static inline void hda_bus_ml_free(struct hdac_bus *bus) { } static inline int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; }
+static inline void +hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) { } + +static inline bool +hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 93d532acd1c2..cd5bbfa3684d 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -232,6 +232,28 @@ static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink) return check_power_active(lctl, sublink, false); }
+static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable) +{ + u32 val; + + val = readl(lctl); + if (enable) + val |= AZX_ML_LCTL_INTEN; + else + val &= ~AZX_ML_LCTL_INTEN; + + writel(val, lctl); +} + +static bool hdaml_link_check_interrupt(u32 __iomem *lctl) +{ + u32 val; + + val = readl(lctl); + + return val & AZX_ML_LCTL_INTSTS; +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -340,6 +362,46 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) } EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
+void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return; + + if (!h2link->intc) + return; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); + + mutex_unlock(&h2link->eml_lock); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK); + +bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return false; + + if (!h2link->intc) + return false; + + hlink = &h2link->hext_link; + + return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
These helpers configure the ratio between the base clock and the hardware signal used for link synchronization.
The SYNCPRD is written before the first sublink is powered-up. The SYNCPU bit is set, but it will only be cleared after the link is powered-up, hence the implementation with a set/wait pattern.
These helpers are currently only needed by SoundWire support, where the lock is taken at a higher level, so only the _unlocked versions are exposed for now.
Note that the _wait_bit() implementation is similar to previous helpers in drivers/soundwire, but with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with e.g. readl_poll_timeout().
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 27 ++++++++++ sound/soc/sof/intel/hda-mlink.c | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index ebc7c5b07557..7ef1cd9b72ec 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -17,6 +17,12 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable); bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd); +int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd); + +int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid); +int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
@@ -47,6 +53,27 @@ hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool ena static inline bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; }
+static inline int +hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) +{ + return 0; +} + +static inline int +hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) +{ + return 0; +} + +static inline int +hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + return 0; +} + +static inline int +hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index cd5bbfa3684d..1515c2327130 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -254,6 +254,46 @@ static bool hdaml_link_check_interrupt(u32 __iomem *lctl) return val & AZX_ML_LCTL_INTSTS; }
+static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) +{ + int timeout = HDAML_POLL_DELAY_RETRY; + u32 reg_read; + + do { + reg_read = readl(base + offset); + if ((reg_read & mask) == target) + return 0; + + timeout--; + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + } while (timeout != 0); + + return -EAGAIN; +} + +static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd) +{ + u32 val; + + val = readl(lsync); + val &= ~AZX_REG_ML_LSYNC_SYNCPRD; + val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD); + + /* + * set SYNCPU but do not wait. The bit is cleared by hardware when + * the link becomes active. + */ + val |= AZX_REG_ML_LSYNC_SYNCPU; + + writel(val, lsync); +} + +static int hdaml_link_wait_syncpu(u32 __iomem *lsync) +{ + return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -402,6 +442,56 @@ bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) } EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) +{ + return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
The multi-link synchronization uses the same concept and registers, but moved to the HDAudio extended links.
Add helpers for sync_arm and sync_go which are the basic for the bus reset, bank switch and clock stop.
Since SoundWire is the only user of those helpers, only expose the _unlocked versions for now.
Note that SYNCGO is a write-only bit, so no error can be reported. We still return 0 for compatibility with the SoundWire stream management headers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 18 +++++++++ sound/soc/sof/intel/hda-mlink.c | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 7ef1cd9b72ec..6be0fb63652c 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -23,6 +23,12 @@ int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd); int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid); int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus);
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); +void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink); + +int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid); +int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
@@ -74,6 +80,18 @@ hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) static inline int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; }
+static inline void +hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) { } + +static inline void +hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) { } + +static inline int +hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) { return 0; } + +static inline int +hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 1515c2327130..19df0253261a 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -294,6 +294,26 @@ static int hdaml_link_wait_syncpu(u32 __iomem *lsync) return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0); }
+static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink) +{ + u32 val; + + val = readl(lsync); + val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink); + + writel(val, lsync); +} + +static void hdaml_link_sync_go(u32 __iomem *lsync) +{ + u32 val; + + val = readl(lsync); + val |= AZX_REG_ML_LSYNC_SYNCGO; + + writel(val, lsync); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -492,6 +512,56 @@ int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return; + + if (!h2link->lss) + return; + + hlink = &h2link->hext_link; + + hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); + +void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) +{ + hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
This helper is an optimization where sync_go is only called when the cmdsync field is actually set to a non-zero value.
Since this is also only used by SoundWire for now, only expose the _unlocked version.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 9 ++++++++ sound/soc/sof/intel/hda-mlink.c | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 6be0fb63652c..64106d439fd8 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -29,6 +29,9 @@ void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink); int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid); int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus);
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid); +bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
@@ -92,6 +95,12 @@ hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) { return static inline int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) { return 0; }
+static inline bool +hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) { return false; } + +static inline bool +hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) { return false; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 19df0253261a..0912989ce729 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -314,6 +314,15 @@ static void hdaml_link_sync_go(u32 __iomem *lsync) writel(val, lsync); }
+static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) +{ + u32 val; + + val = readl(lsync); + + return !!(val & cmdsync_mask); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -562,6 +571,35 @@ int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + u32 cmdsync_mask; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, + AZX_REG_ML_LSYNC_CMDSYNC_SHIFT); + + return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, + cmdsync_mask); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); + +bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) {
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Each SoundWire peripheral can be programmed from the manager side either with a regular command FIFO, or with the HDaudio CORB/RIRB DMA-based mechanism. The mapping between SoundWire peripheral and SDI address is handled with the LSDIID register.
This mapping only works of course if each peripheral has a unique address across all links. This has already been enforced in previous Intel contributions allowing for an IDA-based solution for the device number allocation.
The checks on the dev_num are handled at the SoundWire level, but the locking is handled at the hda-mlink level.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 64106d439fd8..fc63aec97119 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -41,6 +41,8 @@ int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, i int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink); int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink);
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -131,6 +133,9 @@ hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) { return 0 static inline int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; }
+static inline int +hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; } + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 0912989ce729..87e6c5f2f3ea 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -323,6 +323,16 @@ static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) return !!(val & cmdsync_mask); }
+static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) +{ + u32 val; + + val = readl(lsdiid); + val |= BIT(dev_num); + + writel(val, lsdiid); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -698,6 +708,26 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + if (!h2link) + return -ENODEV; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); + + mutex_unlock(&h2link->eml_lock); + + return 0; +} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink;
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
Small helpers to make DAI ops simpler.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 10 ++++++++++ sound/soc/sof/intel/hda-mlink.c | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index fc63aec97119..c3f31fa85104 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -7,6 +7,7 @@ */
struct hdac_bus; +struct hdac_ext_link;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
@@ -48,6 +49,9 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus);
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); +struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); + #else
static inline int @@ -141,4 +145,10 @@ static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
+static inline struct hdac_ext_link * +hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } + +static inline struct hdac_ext_link * +hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; } + #endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 87e6c5f2f3ea..2e0ca6eb322d 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -788,6 +788,30 @@ int hda_bus_ml_suspend(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP); + if (!h2link) + return NULL; + + return &h2link->hext_link; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK); + +struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); + if (!h2link) + return NULL; + + return &h2link->hext_link; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK); + #endif
MODULE_LICENSE("Dual BSD/GPL");
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
For DMIC and SSP, the DSP will be responsible for programming the blobs and link registers.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 7 +++++++ sound/soc/sof/intel/hda-mlink.c | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index c3f31fa85104..17adb748597a 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -52,6 +52,8 @@ int hda_bus_ml_suspend(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable); + #else
static inline int @@ -151,4 +153,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } static inline struct hdac_ext_link * hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; }
+static inline int +hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + return 0; +} #endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 2e0ca6eb322d..e38f00bd72ad 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -333,6 +333,18 @@ static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) writel(val, lsdiid); }
+static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable) +{ + u32 val = readl(lctl); + + if (enable) + val |= AZX_ML_LCTL_OFLEN; + else + val &= ~AZX_ML_LCTL_OFLEN; + + writel(val, lctl); +} + /* END HDAML section */
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -812,6 +824,30 @@ struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (!h2link->ofls) + return 0; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); + + mutex_unlock(&h2link->eml_lock); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK); + #endif
MODULE_LICENSE("Dual BSD/GPL");
From: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com
For SoundWire usages, we need to use the global eml_lock to serialize/protect all accesses to shared registers. Due to the split implementation across two subsystems, we need to pass a pointer around.
Signed-off-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 17 insertions(+)
diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 17adb748597a..7b6ad0429034 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -52,6 +52,8 @@ int hda_bus_ml_suspend(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid); + int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable);
#else @@ -153,6 +155,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } static inline struct hdac_ext_link * hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; }
+static inline struct mutex * +hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) { return NULL; } + static inline int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index e38f00bd72ad..a3e5af7319ae 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -800,6 +800,18 @@ int hda_bus_ml_suspend(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return NULL; + + return &h2link->eml_lock; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK); + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { struct hdac_ext2_link *h2link;
participants (4)
-
Amadeusz Sławiński
-
Peter Ujfalusi
-
Pierre-Louis Bossart
-
Takashi Iwai