1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// 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"
#define MAX_SAMPLE_RATE 192 /* KHz*/
#define MAX_BYTES_PER_SAMPLE 4
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);
}
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_SAMPLE_RATE * MAX_BYTES_PER_SAMPLE *
admaif->soc_data->max_stream_ch * 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;
}
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);
}
MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver");
MODULE_LICENSE("GPL");
|