Skip to content

Commit 4a91fe4

Browse files
Sheetalbroonie
authored andcommitted
ASoC: tegra: Add interconnect support
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>
1 parent 299ce4b commit 4a91fe4

File tree

5 files changed

+192
-6
lines changed

5 files changed

+192
-6
lines changed

sound/soc/tegra/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ snd-soc-tegra210-dmic-y := tegra210_dmic.o
1313
snd-soc-tegra210-i2s-y := tegra210_i2s.o
1414
snd-soc-tegra186-asrc-y := tegra186_asrc.o
1515
snd-soc-tegra186-dspk-y := tegra186_dspk.o
16-
snd-soc-tegra210-admaif-y := tegra210_admaif.o
16+
snd-soc-tegra210-admaif-y := tegra210_admaif.o tegra_isomgr_bw.o
1717
snd-soc-tegra210-mvc-y := tegra210_mvc.o
1818
snd-soc-tegra210-sfc-y := tegra210_sfc.o
1919
snd-soc-tegra210-amx-y := tegra210_amx.o

sound/soc/tegra/tegra210_admaif.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: GPL-2.0-only
2-
// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES.
2+
// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
33
// All rights reserved.
44
//
55
// tegra210_admaif.c - Tegra ADMAIF driver
@@ -13,6 +13,7 @@
1313
#include <linux/regmap.h>
1414
#include <sound/pcm_params.h>
1515
#include <sound/soc.h>
16+
#include "tegra_isomgr_bw.h"
1617
#include "tegra210_admaif.h"
1718
#include "tegra_cif.h"
1819
#include "tegra_pcm.h"
@@ -262,6 +263,18 @@ static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg,
262263
return 0;
263264
}
264265

266+
static int tegra_admaif_prepare(struct snd_pcm_substream *substream,
267+
struct snd_soc_dai *dai)
268+
{
269+
return tegra_isomgr_adma_setbw(substream, dai, true);
270+
}
271+
272+
static void tegra_admaif_shutdown(struct snd_pcm_substream *substream,
273+
struct snd_soc_dai *dai)
274+
{
275+
tegra_isomgr_adma_setbw(substream, dai, false);
276+
}
277+
265278
static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
266279
struct snd_pcm_hw_params *params,
267280
struct snd_soc_dai *dai)
@@ -554,6 +567,8 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
554567
.probe = tegra_admaif_dai_probe,
555568
.hw_params = tegra_admaif_hw_params,
556569
.trigger = tegra_admaif_trigger,
570+
.shutdown = tegra_admaif_shutdown,
571+
.prepare = tegra_admaif_prepare,
557572
};
558573

559574
#define DAI(dai_name) \
@@ -800,6 +815,12 @@ static int tegra_admaif_probe(struct platform_device *pdev)
800815

801816
regcache_cache_only(admaif->regmap, true);
802817

818+
err = tegra_isomgr_adma_register(&pdev->dev);
819+
if (err) {
820+
dev_err(&pdev->dev, "Failed to add interconnect path\n");
821+
return err;
822+
}
823+
803824
regmap_update_bits(admaif->regmap, admaif->soc_data->global_base +
804825
TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1);
805826

@@ -851,6 +872,7 @@ static int tegra_admaif_probe(struct platform_device *pdev)
851872

852873
static void tegra_admaif_remove(struct platform_device *pdev)
853874
{
875+
tegra_isomgr_adma_unregister(&pdev->dev);
854876
pm_runtime_disable(&pdev->dev);
855877
}
856878

sound/soc/tegra/tegra210_admaif.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
/* SPDX-License-Identifier: GPL-2.0-only */
2-
/*
3-
* tegra210_admaif.h - Tegra ADMAIF registers
1+
/* SPDX-License-Identifier: GPL-2.0-only
2+
* SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
3+
* All rights reserved.
44
*
5-
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
5+
* tegra210_admaif.h - Tegra ADMAIF registers
66
*
77
*/
88

@@ -157,6 +157,7 @@ struct tegra_admaif {
157157
unsigned int *mono_to_stereo[ADMAIF_PATHS];
158158
unsigned int *stereo_to_mono[ADMAIF_PATHS];
159159
struct regmap *regmap;
160+
struct tegra_adma_isomgr *adma_isomgr;
160161
};
161162

162163
#endif

sound/soc/tegra/tegra_isomgr_bw.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
3+
// All rights reserved.
4+
//
5+
// ADMA bandwidth calculation
6+
7+
#include <linux/interconnect.h>
8+
#include <linux/module.h>
9+
#include <sound/pcm_params.h>
10+
#include <sound/soc.h>
11+
#include "tegra_isomgr_bw.h"
12+
#include "tegra210_admaif.h"
13+
14+
/* Max possible rate is 192KHz x 16channel x 4bytes */
15+
#define MAX_BW_PER_DEV 12288
16+
17+
int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
18+
struct snd_soc_dai *dai, bool is_running)
19+
{
20+
struct device *dev = dai->dev;
21+
struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
22+
struct tegra_adma_isomgr *adma_isomgr = admaif->adma_isomgr;
23+
struct snd_pcm_runtime *runtime = substream->runtime;
24+
struct snd_pcm *pcm = substream->pcm;
25+
u32 type = substream->stream, bandwidth = 0;
26+
int sample_bytes;
27+
28+
if (!adma_isomgr)
29+
return 0;
30+
31+
if (!runtime || !pcm)
32+
return -EINVAL;
33+
34+
if (pcm->device >= adma_isomgr->max_pcm_device) {
35+
dev_err(dev, "%s: PCM device number %d is greater than %d\n", __func__,
36+
pcm->device, adma_isomgr->max_pcm_device);
37+
return -EINVAL;
38+
}
39+
40+
/*
41+
* No action if stream is running and bandwidth is already set or
42+
* stream is not running and bandwidth is already reset
43+
*/
44+
if ((adma_isomgr->bw_per_dev[type][pcm->device] && is_running) ||
45+
(!adma_isomgr->bw_per_dev[type][pcm->device] && !is_running))
46+
return 0;
47+
48+
if (is_running) {
49+
sample_bytes = snd_pcm_format_width(runtime->format) / 8;
50+
if (sample_bytes < 0)
51+
return sample_bytes;
52+
53+
/* KB/s kilo bytes per sec */
54+
bandwidth = runtime->channels * (runtime->rate / 1000) *
55+
sample_bytes;
56+
}
57+
58+
mutex_lock(&adma_isomgr->mutex);
59+
60+
if (is_running) {
61+
if (bandwidth + adma_isomgr->current_bandwidth > adma_isomgr->max_bw)
62+
bandwidth = adma_isomgr->max_bw - adma_isomgr->current_bandwidth;
63+
64+
adma_isomgr->current_bandwidth += bandwidth;
65+
} else {
66+
adma_isomgr->current_bandwidth -= adma_isomgr->bw_per_dev[type][pcm->device];
67+
}
68+
69+
mutex_unlock(&adma_isomgr->mutex);
70+
71+
adma_isomgr->bw_per_dev[type][pcm->device] = bandwidth;
72+
73+
dev_dbg(dev, "Setting up bandwidth to %d KBps\n", adma_isomgr->current_bandwidth);
74+
75+
return icc_set_bw(adma_isomgr->icc_path_handle,
76+
adma_isomgr->current_bandwidth, adma_isomgr->max_bw);
77+
}
78+
EXPORT_SYMBOL(tegra_isomgr_adma_setbw);
79+
80+
int tegra_isomgr_adma_register(struct device *dev)
81+
{
82+
struct tegra_admaif *admaif = dev_get_drvdata(dev);
83+
struct tegra_adma_isomgr *adma_isomgr;
84+
int i;
85+
86+
adma_isomgr = devm_kzalloc(dev, sizeof(struct tegra_adma_isomgr), GFP_KERNEL);
87+
if (!adma_isomgr)
88+
return -ENOMEM;
89+
90+
adma_isomgr->icc_path_handle = devm_of_icc_get(dev, "write");
91+
if (IS_ERR(adma_isomgr->icc_path_handle))
92+
return dev_err_probe(dev, PTR_ERR(adma_isomgr->icc_path_handle),
93+
"failed to acquire interconnect path\n");
94+
95+
/* Either INTERCONNECT config OR interconnect property is not defined */
96+
if (!adma_isomgr->icc_path_handle) {
97+
devm_kfree(dev, adma_isomgr);
98+
return 0;
99+
}
100+
101+
adma_isomgr->max_pcm_device = admaif->soc_data->num_ch;
102+
adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device;
103+
104+
for (i = 0; i < STREAM_TYPE; i++) {
105+
adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device *
106+
sizeof(u32), GFP_KERNEL);
107+
if (!adma_isomgr->bw_per_dev[i])
108+
return -ENOMEM;
109+
}
110+
111+
adma_isomgr->current_bandwidth = 0;
112+
mutex_init(&adma_isomgr->mutex);
113+
admaif->adma_isomgr = adma_isomgr;
114+
115+
return 0;
116+
}
117+
EXPORT_SYMBOL(tegra_isomgr_adma_register);
118+
119+
void tegra_isomgr_adma_unregister(struct device *dev)
120+
{
121+
struct tegra_admaif *admaif = dev_get_drvdata(dev);
122+
123+
if (!admaif->adma_isomgr)
124+
return;
125+
126+
mutex_destroy(&admaif->adma_isomgr->mutex);
127+
}
128+
EXPORT_SYMBOL(tegra_isomgr_adma_unregister);
129+
130+
MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
131+
MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver");
132+
MODULE_LICENSE("GPL");

sound/soc/tegra/tegra_isomgr_bw.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
3+
* All rights reserved.
4+
*
5+
* tegra_isomgr_bw.h - Definitions for ADMA bandwidth calculation
6+
*
7+
*/
8+
9+
#ifndef __TEGRA_ISOMGR_BW_H__
10+
#define __TEGRA_ISOMGR_BW_H__
11+
12+
/* Playback and Capture streams */
13+
#define STREAM_TYPE 2
14+
15+
struct tegra_adma_isomgr {
16+
/* Protect pcm devices bandwidth */
17+
struct mutex mutex;
18+
/* interconnect path handle */
19+
struct icc_path *icc_path_handle;
20+
u32 *bw_per_dev[STREAM_TYPE];
21+
u32 current_bandwidth;
22+
u32 max_pcm_device;
23+
u32 max_bw;
24+
};
25+
26+
int tegra_isomgr_adma_register(struct device *dev);
27+
void tegra_isomgr_adma_unregister(struct device *dev);
28+
int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
29+
struct snd_soc_dai *dai, bool is_running);
30+
31+
#endif

0 commit comments

Comments
 (0)