diff options
-rw-r--r-- | sound/soc/tegra/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_admaif.c | 24 | ||||
-rw-r--r-- | sound/soc/tegra/tegra210_admaif.h | 9 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_isomgr_bw.c | 132 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_isomgr_bw.h | 31 |
5 files changed, 192 insertions, 6 deletions
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index cea4b0d54378..defea7f53f11 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -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 diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 58fdb0e79954..f56d1e03239d 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -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); } diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h index 96686dc92081..748f886ee74e 100644 --- a/sound/soc/tegra/tegra210_admaif.h +++ b/sound/soc/tegra/tegra210_admaif.h @@ -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 index 000000000000..7789efe13873 --- /dev/null +++ b/sound/soc/tegra/tegra_isomgr_bw.c @@ -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 index 000000000000..86db3cfd4e43 --- /dev/null +++ b/sound/soc/tegra/tegra_isomgr_bw.h @@ -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 |