mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
ASoC: tegra: Add Tegra186 based DSPK driver
The Digital Speaker Controller (DSPK) converts the multi-bit Pulse Code Modulation (PCM) audio input to oversampled 1-bit Pulse Density Modulation (PDM) output. From the signal flow perpsective, the DSPK can be viewed as a PDM transmitter that up-samples the input to the desired sampling rate by interpolation then converts the oversampled PCM input to the desired 1-bit output via Delta Sigma Modulation (DSM). This patch registers DSPK component with ASoC framework. The component driver exposes DAPM widgets, routes and kcontrols for the device. The DAI driver exposes DSPK interfaces, which can be used to connect different components in the ASoC layer. Makefile and Kconfig support is added to allow to build the driver. The DSPK devices can be enabled in the DT via "nvidia,tegra186-dspk" compatible binding. This driver can be used on Tegra194 chip as well. Signed-off-by: Sameer Pujar <spujar@nvidia.com> Link: https://lore.kernel.org/r/1595134890-16470-7-git-send-email-spujar@nvidia.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* tegra186_dspk.c - Tegra186 DSPK driver
|
||||
*
|
||||
* Copyright (c) 2015-2020 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
*/
|
||||
//
|
||||
// tegra186_dspk.c - Tegra186 DSPK driver
|
||||
//
|
||||
// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
@@ -40,14 +38,8 @@ static int tegra186_dspk_get_control(struct snd_kcontrol *kcontrol,
|
||||
ucontrol->value.integer.value[0] = dspk->osr_val;
|
||||
else if (strstr(kcontrol->id.name, "LR Polarity Select"))
|
||||
ucontrol->value.integer.value[0] = dspk->lrsel;
|
||||
else if (strstr(kcontrol->id.name, "Sample Rate"))
|
||||
ucontrol->value.integer.value[0] = dspk->srate_override;
|
||||
else if (strstr(kcontrol->id.name, "Audio Channels"))
|
||||
ucontrol->value.integer.value[0] = dspk->audio_ch_override;
|
||||
else if (strstr(kcontrol->id.name, "Channel Select"))
|
||||
ucontrol->value.integer.value[0] = dspk->ch_sel;
|
||||
else if (strstr(kcontrol->id.name, "Audio Bit Format"))
|
||||
ucontrol->value.integer.value[0] = dspk->audio_fmt_override;
|
||||
else if (strstr(kcontrol->id.name, "Mono To Stereo"))
|
||||
ucontrol->value.integer.value[0] = dspk->mono_to_stereo;
|
||||
else if (strstr(kcontrol->id.name, "Stereo To Mono"))
|
||||
@@ -69,14 +61,8 @@ static int tegra186_dspk_put_control(struct snd_kcontrol *kcontrol,
|
||||
dspk->osr_val = val;
|
||||
else if (strstr(kcontrol->id.name, "LR Polarity Select"))
|
||||
dspk->lrsel = val;
|
||||
else if (strstr(kcontrol->id.name, "Sample Rate"))
|
||||
dspk->srate_override = val;
|
||||
else if (strstr(kcontrol->id.name, "Audio Channels"))
|
||||
dspk->audio_ch_override = val;
|
||||
else if (strstr(kcontrol->id.name, "Channel Select"))
|
||||
dspk->ch_sel = val;
|
||||
else if (strstr(kcontrol->id.name, "Audio Bit Format"))
|
||||
dspk->audio_fmt_override = val;
|
||||
else if (strstr(kcontrol->id.name, "Mono To Stereo"))
|
||||
dspk->mono_to_stereo = val;
|
||||
else if (strstr(kcontrol->id.name, "Stereo To Mono"))
|
||||
@@ -114,12 +100,6 @@ static int tegra186_dspk_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned int tegra186_dspk_fmts[] = {
|
||||
0,
|
||||
TEGRA_ACIF_BITS_16,
|
||||
TEGRA_ACIF_BITS_32,
|
||||
};
|
||||
|
||||
static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
@@ -136,10 +116,6 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
|
||||
channels = params_channels(params);
|
||||
cif_conf.audio_ch = channels;
|
||||
|
||||
/* Override audio channel */
|
||||
if (dspk->audio_ch_override)
|
||||
cif_conf.audio_ch = dspk->audio_ch_override;
|
||||
|
||||
/* Client channel */
|
||||
switch (dspk->ch_sel) {
|
||||
case DSPK_CH_SELECT_LEFT:
|
||||
@@ -159,25 +135,16 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
|
||||
cif_conf.client_bits = TEGRA_ACIF_BITS_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported format!\n");
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Audio bit format override */
|
||||
if (dspk->audio_fmt_override)
|
||||
cif_conf.audio_bits =
|
||||
tegra186_dspk_fmts[dspk->audio_fmt_override];
|
||||
|
||||
srate = params_rate(params);
|
||||
/* Sample rate override */
|
||||
if (dspk->srate_override)
|
||||
srate = dspk->srate_override;
|
||||
|
||||
/* RX FIFO threshold in terms of frames */
|
||||
max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
|
||||
@@ -227,21 +194,11 @@ static const struct snd_soc_dai_ops tegra186_dspk_dai_ops = {
|
||||
.hw_params = tegra186_dspk_hw_params,
|
||||
};
|
||||
|
||||
/*
|
||||
* Three DAIs are exposed
|
||||
* 1. "CIF" DAI for connecting with XBAR
|
||||
* 2. "DAP" DAI for connecting with CODEC
|
||||
* 3. "DUMMY_SINK" can be used when no external
|
||||
* codec connection is available. In such case
|
||||
* "DAP" is connected with "DUMMY_SINK"
|
||||
* Order of these DAIs should not be changed, since DAI links in DT refer
|
||||
* to these DAIs depending on the index.
|
||||
*/
|
||||
static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
|
||||
{
|
||||
.name = "CIF",
|
||||
.name = "DSPK-CIF",
|
||||
.playback = {
|
||||
.stream_name = "CIF Receive",
|
||||
.stream_name = "CIF-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
@@ -250,9 +207,9 @@ static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "DAP",
|
||||
.capture = {
|
||||
.stream_name = "DAP Transmit",
|
||||
.name = "DSPK-DAP",
|
||||
.playback = {
|
||||
.stream_name = "DAP-Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
@@ -262,70 +219,21 @@ static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
|
||||
.ops = &tegra186_dspk_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
},
|
||||
/* The second DAI is used when the output of the DSPK is connected
|
||||
* to two mono codecs. When the output of the DSPK is connected to
|
||||
* a single stereo codec, then only the first DAI should be used.
|
||||
*/
|
||||
{
|
||||
.name = "CIF2",
|
||||
.playback = {
|
||||
.stream_name = "CIF2 Receive",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "DAP2",
|
||||
.capture = {
|
||||
.stream_name = "DAP2 Transmit",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
.symmetric_rates = 1,
|
||||
},
|
||||
{
|
||||
.name = "DUMMY_SINK",
|
||||
.playback = {
|
||||
.stream_name = "Dummy Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = {
|
||||
SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("DAP2 TX", NULL, 0, 0, 0, 0),
|
||||
SND_SOC_DAPM_SPK("Dummy Output", NULL),
|
||||
SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
|
||||
SND_SOC_DAPM_SPK("SPK", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {
|
||||
{ "DAP TX", NULL, "CIF Receive" },
|
||||
{ "DAP Transmit", NULL, "DAP TX" },
|
||||
{ "DAP2 TX", NULL, "CIF2 Receive" },
|
||||
{ "DAP2 Transmit", NULL, "DAP2 TX" },
|
||||
{ "Dummy Output", NULL, "Dummy Playback" },
|
||||
{ "XBAR-Playback", NULL, "XBAR-TX" },
|
||||
{ "CIF-Playback", NULL, "XBAR-Playback" },
|
||||
{ "RX", NULL, "CIF-Playback" },
|
||||
{ "DAP-Playback", NULL, "RX" },
|
||||
{ "SPK", NULL, "DAP-Playback" },
|
||||
};
|
||||
|
||||
static const char * const tegra186_dspk_format_text[] = {
|
||||
"None",
|
||||
"16",
|
||||
"32",
|
||||
};
|
||||
|
||||
static const struct soc_enum tegra186_dspk_format_enum =
|
||||
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_format_text),
|
||||
tegra186_dspk_format_text);
|
||||
|
||||
static const char * const tegra186_dspk_ch_sel_text[] = {
|
||||
"Left", "Right", "Stereo",
|
||||
};
|
||||
@@ -347,7 +255,7 @@ static const char * const tegra186_dspk_lrsel_text[] = {
|
||||
};
|
||||
|
||||
static const char * const tegra186_dspk_mono_conv_text[] = {
|
||||
"ZERO", "COPY",
|
||||
"Zero", "Copy",
|
||||
};
|
||||
|
||||
static const struct soc_enum tegra186_dspk_mono_conv_enum =
|
||||
@@ -376,14 +284,8 @@ static const struct snd_kcontrol_new tegrat186_dspk_controls[] = {
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_SINGLE_EXT("Sample Rate", SND_SOC_NOPM, 0, 48000, 0,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_SINGLE_EXT("Audio Channels", SND_SOC_NOPM, 0, 2, 0,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_ENUM_EXT("Audio Bit Format", tegra186_dspk_format_enum,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,
|
||||
tegra186_dspk_get_control, tegra186_dspk_put_control),
|
||||
SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum,
|
||||
@@ -473,7 +375,7 @@ static int tegra186_dspk_platform_probe(struct platform_device *pdev)
|
||||
dspk->osr_val = DSPK_OSR_64;
|
||||
dspk->lrsel = DSPK_LRSEL_LEFT;
|
||||
dspk->ch_sel = DSPK_CH_SELECT_STEREO;
|
||||
dspk->mono_to_stereo = 0; /* "ZERO" */
|
||||
dspk->mono_to_stereo = 0; /* "Zero" */
|
||||
|
||||
dev_set_drvdata(dev, dspk);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* tegra186_dspk.h - Definitions for Tegra186 DSPK driver
|
||||
*
|
||||
* Copyright (c) 2015-2020 NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -60,10 +60,7 @@ struct tegra186_dspk {
|
||||
unsigned int rx_fifo_th;
|
||||
unsigned int osr_val;
|
||||
unsigned int lrsel;
|
||||
unsigned int srate_override;
|
||||
unsigned int audio_ch_override;
|
||||
unsigned int ch_sel; /* Used for client channel override */
|
||||
unsigned int audio_fmt_override;
|
||||
unsigned int ch_sel;
|
||||
unsigned int mono_to_stereo;
|
||||
unsigned int stereo_to_mono;
|
||||
struct clk *clk_dspk;
|
||||
|
||||
Reference in New Issue
Block a user