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