diff mbox series

ASoC: tegra: Add interconnect support

Message ID 20250121110928.1799377-1-sheetal@nvidia.com (mailing list archive)
State New
Headers show
Series ASoC: tegra: Add interconnect support | expand

Commit Message

Sheetal . Jan. 21, 2025, 11:09 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>
---
 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
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..539c989514db
--- /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, pcm_id;
+	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_id] && is_running) ||
+	    (!adma_isomgr->bw_per_dev[type][pcm_id] && !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_id];
+	}
+
+	mutex_unlock(&adma_isomgr->mutex);
+
+	adma_isomgr->bw_per_dev[type][pcm_id] = 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