diff mbox series

[V2] ASoC: tegra: Add interconnect support

Message ID 20250203105304.4155542-1-sheetal@nvidia.com (mailing list archive)
State Accepted
Commit 4a91fe4c0d683c56044579fb263c660f5d18efac
Headers show
Series [V2] ASoC: tegra: Add interconnect support | expand

Commit Message

Sheetal . Feb. 3, 2025, 10:53 a.m. UTC
From: Sheetal <sheetal@nvidia.com>

Add interconnect framework support to set required audio bandwidth
based on PCM device usage. The maximum bandwidth is determined by
the number of APE PCM devices and maximum audio format supported.

If interconnect property is not defined or INTERCONNECT config
is not enabled then the audio usecase will still function.

Validate bandwidth updates by reading the interconnect summary sysfs
node during PCM device open and close operations.

Signed-off-by: Sheetal <sheetal@nvidia.com>

---
v2: Fix pcm_id parameter uninitialized warning.
---
 sound/soc/tegra/Makefile          |   2 +-
 sound/soc/tegra/tegra210_admaif.c |  24 +++++-
 sound/soc/tegra/tegra210_admaif.h |   9 +-
 sound/soc/tegra/tegra_isomgr_bw.c | 132 ++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra_isomgr_bw.h |  31 +++++++
 5 files changed, 192 insertions(+), 6 deletions(-)
 create mode 100644 sound/soc/tegra/tegra_isomgr_bw.c
 create mode 100644 sound/soc/tegra/tegra_isomgr_bw.h

Comments

Mark Brown Feb. 5, 2025, 12:44 p.m. UTC | #1
On Mon, 03 Feb 2025 10:53:04 +0000, Sheetal . wrote:
> Add interconnect framework support to set required audio bandwidth
> based on PCM device usage. The maximum bandwidth is determined by
> the number of APE PCM devices and maximum audio format supported.
> 
> If interconnect property is not defined or INTERCONNECT config
> is not enabled then the audio usecase will still function.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/1] ASoC: tegra: Add interconnect support
      commit: 4a91fe4c0d683c56044579fb263c660f5d18efac

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
diff mbox series

Patch

diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index cea4b0d54378..defea7f53f11 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -13,7 +13,7 @@  snd-soc-tegra210-dmic-y := tegra210_dmic.o
 snd-soc-tegra210-i2s-y := tegra210_i2s.o
 snd-soc-tegra186-asrc-y := tegra186_asrc.o
 snd-soc-tegra186-dspk-y := tegra186_dspk.o
-snd-soc-tegra210-admaif-y := tegra210_admaif.o
+snd-soc-tegra210-admaif-y := tegra210_admaif.o tegra_isomgr_bw.o
 snd-soc-tegra210-mvc-y := tegra210_mvc.o
 snd-soc-tegra210-sfc-y := tegra210_sfc.o
 snd-soc-tegra210-amx-y := tegra210_amx.o
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index 58fdb0e79954..f56d1e03239d 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -1,5 +1,5 @@ 
 // SPDX-License-Identifier: GPL-2.0-only
-// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES.
+// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
 // All rights reserved.
 //
 // tegra210_admaif.c - Tegra ADMAIF driver
@@ -13,6 +13,7 @@ 
 #include <linux/regmap.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include "tegra_isomgr_bw.h"
 #include "tegra210_admaif.h"
 #include "tegra_cif.h"
 #include "tegra_pcm.h"
@@ -262,6 +263,18 @@  static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg,
 	return 0;
 }
 
+static int tegra_admaif_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	return tegra_isomgr_adma_setbw(substream, dai, true);
+}
+
+static void tegra_admaif_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	tegra_isomgr_adma_setbw(substream, dai, false);
+}
+
 static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
 				  struct snd_pcm_hw_params *params,
 				  struct snd_soc_dai *dai)
@@ -554,6 +567,8 @@  static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
 	.probe		= tegra_admaif_dai_probe,
 	.hw_params	= tegra_admaif_hw_params,
 	.trigger	= tegra_admaif_trigger,
+	.shutdown	= tegra_admaif_shutdown,
+	.prepare	= tegra_admaif_prepare,
 };
 
 #define DAI(dai_name)					\
@@ -800,6 +815,12 @@  static int tegra_admaif_probe(struct platform_device *pdev)
 
 	regcache_cache_only(admaif->regmap, true);
 
+	err = tegra_isomgr_adma_register(&pdev->dev);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to add interconnect path\n");
+		return err;
+	}
+
 	regmap_update_bits(admaif->regmap, admaif->soc_data->global_base +
 			   TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1);
 
@@ -851,6 +872,7 @@  static int tegra_admaif_probe(struct platform_device *pdev)
 
 static void tegra_admaif_remove(struct platform_device *pdev)
 {
+	tegra_isomgr_adma_unregister(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 }
 
diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h
index 96686dc92081..748f886ee74e 100644
--- a/sound/soc/tegra/tegra210_admaif.h
+++ b/sound/soc/tegra/tegra210_admaif.h
@@ -1,8 +1,8 @@ 
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * tegra210_admaif.h - Tegra ADMAIF registers
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
+ * All rights reserved.
  *
- * Copyright (c) 2020 NVIDIA CORPORATION.  All rights reserved.
+ * tegra210_admaif.h - Tegra ADMAIF registers
  *
  */
 
@@ -157,6 +157,7 @@  struct tegra_admaif {
 	unsigned int *mono_to_stereo[ADMAIF_PATHS];
 	unsigned int *stereo_to_mono[ADMAIF_PATHS];
 	struct regmap *regmap;
+	struct tegra_adma_isomgr *adma_isomgr;
 };
 
 #endif
diff --git a/sound/soc/tegra/tegra_isomgr_bw.c b/sound/soc/tegra/tegra_isomgr_bw.c
new file mode 100644
index 000000000000..7789efe13873
--- /dev/null
+++ b/sound/soc/tegra/tegra_isomgr_bw.c
@@ -0,0 +1,132 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
+// All rights reserved.
+//
+// ADMA bandwidth calculation
+
+#include <linux/interconnect.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "tegra_isomgr_bw.h"
+#include "tegra210_admaif.h"
+
+/* Max possible rate is 192KHz x 16channel x 4bytes */
+#define MAX_BW_PER_DEV 12288
+
+int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai, bool is_running)
+{
+	struct device *dev = dai->dev;
+	struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+	struct tegra_adma_isomgr *adma_isomgr = admaif->adma_isomgr;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_pcm *pcm = substream->pcm;
+	u32 type = substream->stream, bandwidth = 0;
+	int sample_bytes;
+
+	if (!adma_isomgr)
+		return 0;
+
+	if (!runtime || !pcm)
+		return -EINVAL;
+
+	if (pcm->device >= adma_isomgr->max_pcm_device) {
+		dev_err(dev, "%s: PCM device number %d is greater than %d\n", __func__,
+			pcm->device, adma_isomgr->max_pcm_device);
+		return -EINVAL;
+	}
+
+	/*
+	 * No action if  stream is running and bandwidth is already set or
+	 * stream is not running and bandwidth is already reset
+	 */
+	if ((adma_isomgr->bw_per_dev[type][pcm->device] && is_running) ||
+	    (!adma_isomgr->bw_per_dev[type][pcm->device] && !is_running))
+		return 0;
+
+	if (is_running) {
+		sample_bytes = snd_pcm_format_width(runtime->format) / 8;
+		if (sample_bytes < 0)
+			return sample_bytes;
+
+		/* KB/s kilo bytes per sec */
+		bandwidth = runtime->channels * (runtime->rate / 1000) *
+				sample_bytes;
+	}
+
+	mutex_lock(&adma_isomgr->mutex);
+
+	if (is_running) {
+		if (bandwidth + adma_isomgr->current_bandwidth > adma_isomgr->max_bw)
+			bandwidth = adma_isomgr->max_bw - adma_isomgr->current_bandwidth;
+
+		adma_isomgr->current_bandwidth += bandwidth;
+	} else {
+		adma_isomgr->current_bandwidth -= adma_isomgr->bw_per_dev[type][pcm->device];
+	}
+
+	mutex_unlock(&adma_isomgr->mutex);
+
+	adma_isomgr->bw_per_dev[type][pcm->device] = bandwidth;
+
+	dev_dbg(dev, "Setting up bandwidth to %d KBps\n", adma_isomgr->current_bandwidth);
+
+	return icc_set_bw(adma_isomgr->icc_path_handle,
+			  adma_isomgr->current_bandwidth, adma_isomgr->max_bw);
+}
+EXPORT_SYMBOL(tegra_isomgr_adma_setbw);
+
+int tegra_isomgr_adma_register(struct device *dev)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+	struct tegra_adma_isomgr *adma_isomgr;
+	int i;
+
+	adma_isomgr = devm_kzalloc(dev, sizeof(struct tegra_adma_isomgr), GFP_KERNEL);
+	if (!adma_isomgr)
+		return -ENOMEM;
+
+	adma_isomgr->icc_path_handle = devm_of_icc_get(dev, "write");
+	if (IS_ERR(adma_isomgr->icc_path_handle))
+		return dev_err_probe(dev, PTR_ERR(adma_isomgr->icc_path_handle),
+				"failed to acquire interconnect path\n");
+
+	/* Either INTERCONNECT config OR interconnect property is not defined */
+	if (!adma_isomgr->icc_path_handle) {
+		devm_kfree(dev, adma_isomgr);
+		return 0;
+	}
+
+	adma_isomgr->max_pcm_device = admaif->soc_data->num_ch;
+	adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device;
+
+	for (i = 0; i < STREAM_TYPE; i++) {
+		adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device *
+							  sizeof(u32), GFP_KERNEL);
+		if (!adma_isomgr->bw_per_dev[i])
+			return -ENOMEM;
+	}
+
+	adma_isomgr->current_bandwidth = 0;
+	mutex_init(&adma_isomgr->mutex);
+	admaif->adma_isomgr = adma_isomgr;
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_isomgr_adma_register);
+
+void tegra_isomgr_adma_unregister(struct device *dev)
+{
+	struct tegra_admaif *admaif = dev_get_drvdata(dev);
+
+	if (!admaif->adma_isomgr)
+		return;
+
+	mutex_destroy(&admaif->adma_isomgr->mutex);
+}
+EXPORT_SYMBOL(tegra_isomgr_adma_unregister);
+
+MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
+MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_isomgr_bw.h b/sound/soc/tegra/tegra_isomgr_bw.h
new file mode 100644
index 000000000000..86db3cfd4e43
--- /dev/null
+++ b/sound/soc/tegra/tegra_isomgr_bw.h
@@ -0,0 +1,31 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
+ * All rights reserved.
+ *
+ * tegra_isomgr_bw.h - Definitions for ADMA bandwidth calculation
+ *
+ */
+
+#ifndef __TEGRA_ISOMGR_BW_H__
+#define __TEGRA_ISOMGR_BW_H__
+
+/* Playback and Capture streams */
+#define STREAM_TYPE 2
+
+struct tegra_adma_isomgr {
+	/* Protect pcm devices bandwidth */
+	struct mutex mutex;
+	/* interconnect path handle */
+	struct icc_path *icc_path_handle;
+	u32 *bw_per_dev[STREAM_TYPE];
+	u32 current_bandwidth;
+	u32 max_pcm_device;
+	u32 max_bw;
+};
+
+int tegra_isomgr_adma_register(struct device *dev);
+void tegra_isomgr_adma_unregister(struct device *dev);
+int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai, bool is_running);
+
+#endif