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
tegra_isomgr_adma_setbw(struct snd_pcm_substream * substream,struct snd_soc_dai * dai,bool is_running)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
tegra_isomgr_adma_register(struct device * dev)79 int tegra_isomgr_adma_register(struct device *dev)
80 {
81 struct tegra_admaif *admaif = dev_get_drvdata(dev);
82 struct tegra_adma_isomgr *adma_isomgr;
83 int i;
84
85 adma_isomgr = devm_kzalloc(dev, sizeof(struct tegra_adma_isomgr), GFP_KERNEL);
86 if (!adma_isomgr)
87 return -ENOMEM;
88
89 adma_isomgr->icc_path_handle = devm_of_icc_get(dev, "write");
90 if (IS_ERR(adma_isomgr->icc_path_handle))
91 return dev_err_probe(dev, PTR_ERR(adma_isomgr->icc_path_handle),
92 "failed to acquire interconnect path\n");
93
94 /* Either INTERCONNECT config OR interconnect property is not defined */
95 if (!adma_isomgr->icc_path_handle) {
96 devm_kfree(dev, adma_isomgr);
97 return 0;
98 }
99
100 adma_isomgr->max_pcm_device = admaif->soc_data->num_ch;
101 adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device;
102
103 for (i = 0; i < STREAM_TYPE; i++) {
104 adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device *
105 sizeof(u32), GFP_KERNEL);
106 if (!adma_isomgr->bw_per_dev[i])
107 return -ENOMEM;
108 }
109
110 adma_isomgr->current_bandwidth = 0;
111 mutex_init(&adma_isomgr->mutex);
112 admaif->adma_isomgr = adma_isomgr;
113
114 return 0;
115 }
116
tegra_isomgr_adma_unregister(struct device * dev)117 void tegra_isomgr_adma_unregister(struct device *dev)
118 {
119 struct tegra_admaif *admaif = dev_get_drvdata(dev);
120
121 if (!admaif->adma_isomgr)
122 return;
123
124 mutex_destroy(&admaif->adma_isomgr->mutex);
125 }
126
127 MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
128 MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver");
129 MODULE_LICENSE("GPL");
130