mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
Upstream sound card machine driver is used now and legacy machine driver
is not required anymore. Hence following cleanups are made:
- Remove legacy machine driver 'tegra_machine_driver.c'.
- With legacy driver removal many of the APIs in tegra_asoc_machine.c
are not required. So remove all such APIs and update the header file
as well.
- The ASoC utils 'tegra_asoc_utils.c' is not needed after removal of
legacy machine driver. Remove the corresponding header as well.
- BW manager support needs to be pushed in upstream and it is needed
for ADMAIF and ADSP ASoC drivers. The downstream BW manager code
can't be used by the ADMAIF driver. So remove downstream BW manager
code and put FIXME item in ADSP driver.
Bug 4596865
Change-Id: Iac154b00759571a5593c3d75bbacbae5a68385e2
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3110862
Reviewed-by: Mohan kumar <mkumard@nvidia.com>
Reviewed-by: Dara Ramesh <dramesh@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
360 lines
8.9 KiB
C
360 lines
8.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright (c) 2014-2024 NVIDIA CORPORATION. All rights reserved.
|
|
//
|
|
// tegra_asoc_machine.c - Tegra DAI links parser
|
|
|
|
#include <nvidia/conftest.h>
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/version.h>
|
|
#include <sound/simple_card_utils.h>
|
|
#include <sound/jack.h>
|
|
#include <sound/soc.h>
|
|
#include "tegra_asoc_machine.h"
|
|
|
|
struct tegra_machine_control_data {
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
unsigned int frame_mode;
|
|
unsigned int master_mode;
|
|
};
|
|
|
|
static struct snd_soc_pcm_runtime *tegra_get_be(struct snd_soc_card *card,
|
|
struct snd_soc_dapm_widget *widget,
|
|
int stream)
|
|
{
|
|
struct snd_soc_pcm_runtime *be;
|
|
struct snd_soc_dapm_widget *w;
|
|
struct snd_soc_dai *dai;
|
|
int i;
|
|
|
|
for_each_card_rtds(card, be) {
|
|
for_each_rtd_dais(be, i, dai) {
|
|
w = snd_soc_dai_get_widget(dai, stream);
|
|
if (w == widget)
|
|
return be;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* In case of DPCM DAI link for i2s<->codec, note that the DAIs are
|
|
* not directly mapped. The 'rtd' for codec is different in case of
|
|
* a DPCM DAI link.
|
|
*/
|
|
static int dpcm_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
|
|
unsigned int fmt)
|
|
{
|
|
#if defined(NV_SND_SOC_RTD_TO_CODEC_PRESENT) /* Linux 6.7*/
|
|
struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
|
|
#else
|
|
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
|
|
#endif
|
|
struct snd_soc_dapm_widget_list *list;
|
|
int stream = SNDRV_PCM_STREAM_PLAYBACK;
|
|
struct snd_soc_pcm_runtime *be;
|
|
struct snd_soc_dapm_widget *widget;
|
|
int i, ret;
|
|
|
|
/* nothing to be done if it is a normal sound card */
|
|
if (!rtd->card->component_chaining)
|
|
return 0;
|
|
|
|
snd_soc_dapm_dai_get_connected_widgets(dai, stream, &list, NULL);
|
|
|
|
for_each_dapm_widgets(list, i, widget) {
|
|
if (widget->id != snd_soc_dapm_dai_in &&
|
|
widget->id != snd_soc_dapm_dai_out)
|
|
continue;
|
|
|
|
be = tegra_get_be(rtd->card, widget, stream);
|
|
if (!be)
|
|
continue;
|
|
|
|
ret = snd_soc_runtime_set_dai_fmt(be, fmt);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_codec_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
|
|
unsigned int frame_mode,
|
|
unsigned int master_mode)
|
|
{
|
|
unsigned int fmt = rtd->dai_link->dai_fmt;
|
|
int ret;
|
|
|
|
if (frame_mode) {
|
|
fmt &= ~SND_SOC_DAIFMT_FORMAT_MASK;
|
|
fmt |= frame_mode;
|
|
}
|
|
|
|
if (master_mode) {
|
|
fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
|
master_mode <<= ffs(SND_SOC_DAIFMT_MASTER_MASK) - 1;
|
|
|
|
if (master_mode == SND_SOC_DAIFMT_CBM_CFM)
|
|
fmt |= SND_SOC_DAIFMT_CBM_CFM;
|
|
else
|
|
fmt |= SND_SOC_DAIFMT_CBS_CFS;
|
|
}
|
|
|
|
ret = snd_soc_runtime_set_dai_fmt(rtd, fmt);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return dpcm_runtime_set_dai_fmt(rtd, fmt);
|
|
}
|
|
|
|
/*
|
|
* The order of the below must not be changed as this
|
|
* aligns with the SND_SOC_DAIFMT_XXX definitions in
|
|
* include/sound/soc-dai.h.
|
|
*/
|
|
static const char * const tegra_machine_frame_mode_text[] = {
|
|
"None",
|
|
"i2s",
|
|
"right-j",
|
|
"left-j",
|
|
"dsp-a",
|
|
"dsp-b",
|
|
};
|
|
|
|
static int tegra_machine_codec_get_frame_mode(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct tegra_machine_control_data *data = kcontrol->private_data;
|
|
|
|
ucontrol->value.integer.value[0] = data->frame_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_codec_put_frame_mode(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct tegra_machine_control_data *data = kcontrol->private_data;
|
|
int err;
|
|
|
|
err = tegra_machine_codec_set_dai_fmt(data->rtd,
|
|
ucontrol->value.integer.value[0],
|
|
data->master_mode);
|
|
if (err)
|
|
return err;
|
|
|
|
data->frame_mode = ucontrol->value.integer.value[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char * const tegra_machine_master_mode_text[] = {
|
|
"None",
|
|
"cbm-cfm",
|
|
"cbs-cfs",
|
|
};
|
|
|
|
static int tegra_machine_codec_get_master_mode(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct tegra_machine_control_data *data = kcontrol->private_data;
|
|
|
|
ucontrol->value.integer.value[0] = data->master_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_codec_put_master_mode(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct tegra_machine_control_data *data = kcontrol->private_data;
|
|
int err;
|
|
|
|
err = tegra_machine_codec_set_dai_fmt(data->rtd,
|
|
data->frame_mode,
|
|
ucontrol->value.integer.value[0]);
|
|
if (err)
|
|
return err;
|
|
|
|
data->master_mode = ucontrol->value.integer.value[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct soc_enum tegra_machine_codec_frame_mode =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_frame_mode_text),
|
|
tegra_machine_frame_mode_text);
|
|
|
|
static const struct soc_enum tegra_machine_codec_master_mode =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_master_mode_text),
|
|
tegra_machine_master_mode_text);
|
|
|
|
static int tegra_machine_add_ctl(struct snd_soc_card *card,
|
|
struct snd_kcontrol_new *knew,
|
|
void *private_data,
|
|
const unsigned char *name)
|
|
{
|
|
struct snd_kcontrol *kctl;
|
|
int ret;
|
|
|
|
kctl = snd_ctl_new1(knew, private_data);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
|
|
ret = snd_ctl_add(card->snd_card, kctl);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_add_frame_mode_ctl(struct snd_soc_card *card,
|
|
struct snd_soc_pcm_runtime *rtd, const unsigned char *name,
|
|
struct tegra_machine_control_data *data)
|
|
{
|
|
struct snd_kcontrol_new knew = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = name,
|
|
.info = snd_soc_info_enum_double,
|
|
.index = 0,
|
|
.get = tegra_machine_codec_get_frame_mode,
|
|
.put = tegra_machine_codec_put_frame_mode,
|
|
.private_value =
|
|
(unsigned long)&tegra_machine_codec_frame_mode,
|
|
};
|
|
|
|
return tegra_machine_add_ctl(card, &knew, data, name);
|
|
}
|
|
|
|
static int tegra_machine_add_master_mode_ctl(struct snd_soc_card *card,
|
|
struct snd_soc_pcm_runtime *rtd, const unsigned char *name,
|
|
struct tegra_machine_control_data *data)
|
|
{
|
|
struct snd_kcontrol_new knew = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = name,
|
|
.info = snd_soc_info_enum_double,
|
|
.index = 0,
|
|
.get = tegra_machine_codec_get_master_mode,
|
|
.put = tegra_machine_codec_put_master_mode,
|
|
.private_value =
|
|
(unsigned long)&tegra_machine_codec_master_mode,
|
|
};
|
|
|
|
return tegra_machine_add_ctl(card, &knew, data, name);
|
|
}
|
|
|
|
int tegra_machine_add_i2s_codec_controls(struct snd_soc_card *card)
|
|
{
|
|
struct tegra_machine_control_data *data;
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
struct device_node *np;
|
|
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
|
unsigned int id;
|
|
int ret;
|
|
|
|
list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
np = rtd->dai_link->cpus->of_node;
|
|
|
|
if (!np)
|
|
continue;
|
|
|
|
data = devm_kzalloc(card->dev, sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
data->rtd = rtd;
|
|
data->frame_mode = 0;
|
|
data->master_mode = 0;
|
|
|
|
if (of_property_read_u32(np, "nvidia,ahub-i2s-id", &id) < 0)
|
|
continue;
|
|
|
|
snprintf(name, sizeof(name), "I2S%d codec frame mode", id+1);
|
|
|
|
ret = tegra_machine_add_frame_mode_ctl(card, rtd, name, data);
|
|
if (ret)
|
|
dev_warn(card->dev, "Failed to add control: %s!\n",
|
|
name);
|
|
|
|
snprintf(name, sizeof(name), "I2S%d codec master mode", id+1);
|
|
|
|
ret = tegra_machine_add_master_mode_ctl(card, rtd, name, data);
|
|
if (ret) {
|
|
dev_warn(card->dev, "Failed to add control: %s!\n",
|
|
name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_machine_add_i2s_codec_controls);
|
|
|
|
/*
|
|
* The order of the following definitions should align with
|
|
* the 'snd_jack_types' enum as defined in include/sound/jack.h.
|
|
*/
|
|
static const char * const tegra_machine_jack_state_text[] = {
|
|
"None",
|
|
"HP",
|
|
"MIC",
|
|
"HS",
|
|
};
|
|
|
|
static const struct soc_enum tegra_machine_jack_state =
|
|
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tegra_machine_jack_state_text),
|
|
tegra_machine_jack_state_text);
|
|
|
|
static int tegra_machine_codec_get_jack_state(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_jack *jack = kcontrol->private_data;
|
|
|
|
ucontrol->value.integer.value[0] = jack->status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_machine_codec_put_jack_state(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_jack *jack = kcontrol->private_data;
|
|
|
|
snd_soc_jack_report(jack, ucontrol->value.integer.value[0],
|
|
SND_JACK_HEADSET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tegra_machine_add_codec_jack_control(struct snd_soc_card *card,
|
|
struct snd_soc_pcm_runtime *rtd,
|
|
struct snd_soc_jack *jack)
|
|
{
|
|
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
|
struct snd_kcontrol_new knew = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = name,
|
|
.info = snd_soc_info_enum_double,
|
|
.index = 0,
|
|
.get = tegra_machine_codec_get_jack_state,
|
|
.put = tegra_machine_codec_put_jack_state,
|
|
.private_value = (unsigned long)&tegra_machine_jack_state,
|
|
};
|
|
|
|
if (rtd->dais[rtd->dai_link->num_cpus]->component->name_prefix)
|
|
snprintf(name, sizeof(name), "%s Jack-state",
|
|
rtd->dais[rtd->dai_link->num_cpus]->component->name_prefix);
|
|
else
|
|
snprintf(name, sizeof(name), "Jack-state");
|
|
|
|
return tegra_machine_add_ctl(card, &knew, jack, name);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_machine_add_codec_jack_control);
|
|
|
|
MODULE_DESCRIPTION("Tegra ASoC Machine Utility Code");
|
|
MODULE_LICENSE("GPL v2");
|