]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: tegra: Add interconnect support
authorSheetal <sheetal@nvidia.com>
Mon, 3 Feb 2025 10:53:04 +0000 (10:53 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 3 Feb 2025 14:03:44 +0000 (14:03 +0000)
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>
Link: https://patch.msgid.link/20250203105304.4155542-1-sheetal@nvidia.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/tegra/Makefile
sound/soc/tegra/tegra210_admaif.c
sound/soc/tegra/tegra210_admaif.h
sound/soc/tegra/tegra_isomgr_bw.c [new file with mode: 0644]
sound/soc/tegra/tegra_isomgr_bw.h [new file with mode: 0644]

index cea4b0d543787415d890eff225718824ba68711a..defea7f53f115265d646908e086c2839a7e5a87d 100644 (file)
@@ -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
index 58fdb0e7995428ce2377bb4ea26bd5b879f99c0c..f56d1e03239dfd90e9b407a09d6700e2a81a0618 100644 (file)
@@ -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);
 }
 
index 96686dc92081dada9d1536c8bf57f2e704cc7b12..748f886ee74ee7855dcdea7ee5c8023b2c4d9e30 100644 (file)
@@ -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 (file)
index 0000000..7789efe
--- /dev/null
@@ -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 (file)
index 0000000..86db3cf
--- /dev/null
@@ -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