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:
Sameer Pujar
2020-07-19 10:31:25 +05:30
parent 28e5d4400a
commit 34386d7e14
2 changed files with 21 additions and 122 deletions

View File

@@ -1,10 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* //
* tegra186_dspk.c - Tegra186 DSPK driver // tegra186_dspk.c - Tegra186 DSPK driver
* //
* Copyright (c) 2015-2020 NVIDIA CORPORATION. All rights reserved. // Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
*
*/
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/device.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; ucontrol->value.integer.value[0] = dspk->osr_val;
else if (strstr(kcontrol->id.name, "LR Polarity Select")) else if (strstr(kcontrol->id.name, "LR Polarity Select"))
ucontrol->value.integer.value[0] = dspk->lrsel; 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")) else if (strstr(kcontrol->id.name, "Channel Select"))
ucontrol->value.integer.value[0] = dspk->ch_sel; 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")) else if (strstr(kcontrol->id.name, "Mono To Stereo"))
ucontrol->value.integer.value[0] = dspk->mono_to_stereo; ucontrol->value.integer.value[0] = dspk->mono_to_stereo;
else if (strstr(kcontrol->id.name, "Stereo To Mono")) 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; dspk->osr_val = val;
else if (strstr(kcontrol->id.name, "LR Polarity Select")) else if (strstr(kcontrol->id.name, "LR Polarity Select"))
dspk->lrsel = val; 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")) else if (strstr(kcontrol->id.name, "Channel Select"))
dspk->ch_sel = val; 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")) else if (strstr(kcontrol->id.name, "Mono To Stereo"))
dspk->mono_to_stereo = val; dspk->mono_to_stereo = val;
else if (strstr(kcontrol->id.name, "Stereo To Mono")) else if (strstr(kcontrol->id.name, "Stereo To Mono"))
@@ -114,12 +100,6 @@ static int tegra186_dspk_runtime_resume(struct device *dev)
return 0; 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, static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
@@ -136,10 +116,6 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
channels = params_channels(params); channels = params_channels(params);
cif_conf.audio_ch = channels; cif_conf.audio_ch = channels;
/* Override audio channel */
if (dspk->audio_ch_override)
cif_conf.audio_ch = dspk->audio_ch_override;
/* Client channel */ /* Client channel */
switch (dspk->ch_sel) { switch (dspk->ch_sel) {
case DSPK_CH_SELECT_LEFT: case DSPK_CH_SELECT_LEFT:
@@ -159,25 +135,16 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_16; cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
cif_conf.client_bits = TEGRA_ACIF_BITS_16;
break; break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
cif_conf.audio_bits = TEGRA_ACIF_BITS_32; cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
break; break;
default: default:
dev_err(dev, "unsupported format!\n"); 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); srate = params_rate(params);
/* Sample rate override */
if (dspk->srate_override)
srate = dspk->srate_override;
/* RX FIFO threshold in terms of frames */ /* RX FIFO threshold in terms of frames */
max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1; 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, .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[] = { static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
{ {
.name = "CIF", .name = "DSPK-CIF",
.playback = { .playback = {
.stream_name = "CIF Receive", .stream_name = "CIF-Playback",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000, .rates = SNDRV_PCM_RATE_8000_48000,
@@ -250,9 +207,9 @@ static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
}, },
}, },
{ {
.name = "DAP", .name = "DSPK-DAP",
.capture = { .playback = {
.stream_name = "DAP Transmit", .stream_name = "DAP-Playback",
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000, .rates = SNDRV_PCM_RATE_8000_48000,
@@ -262,70 +219,21 @@ static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
.ops = &tegra186_dspk_dai_ops, .ops = &tegra186_dspk_dai_ops,
.symmetric_rates = 1, .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[] = { 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_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAP2 TX", NULL, 0, 0, 0, 0), SND_SOC_DAPM_SPK("SPK", NULL),
SND_SOC_DAPM_SPK("Dummy Output", NULL),
}; };
static const struct snd_soc_dapm_route tegra186_dspk_routes[] = { static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {
{ "DAP TX", NULL, "CIF Receive" }, { "XBAR-Playback", NULL, "XBAR-TX" },
{ "DAP Transmit", NULL, "DAP TX" }, { "CIF-Playback", NULL, "XBAR-Playback" },
{ "DAP2 TX", NULL, "CIF2 Receive" }, { "RX", NULL, "CIF-Playback" },
{ "DAP2 Transmit", NULL, "DAP2 TX" }, { "DAP-Playback", NULL, "RX" },
{ "Dummy Output", NULL, "Dummy Playback" }, { "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[] = { static const char * const tegra186_dspk_ch_sel_text[] = {
"Left", "Right", "Stereo", "Left", "Right", "Stereo",
}; };
@@ -347,7 +255,7 @@ static const char * const tegra186_dspk_lrsel_text[] = {
}; };
static const char * const tegra186_dspk_mono_conv_text[] = { static const char * const tegra186_dspk_mono_conv_text[] = {
"ZERO", "COPY", "Zero", "Copy",
}; };
static const struct soc_enum tegra186_dspk_mono_conv_enum = 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), tegra186_dspk_get_control, tegra186_dspk_put_control),
SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum, SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,
tegra186_dspk_get_control, tegra186_dspk_put_control), 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, SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,
tegra186_dspk_get_control, tegra186_dspk_put_control), 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, SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,
tegra186_dspk_get_control, tegra186_dspk_put_control), tegra186_dspk_get_control, tegra186_dspk_put_control),
SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum, 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->osr_val = DSPK_OSR_64;
dspk->lrsel = DSPK_LRSEL_LEFT; dspk->lrsel = DSPK_LRSEL_LEFT;
dspk->ch_sel = DSPK_CH_SELECT_STEREO; dspk->ch_sel = DSPK_CH_SELECT_STEREO;
dspk->mono_to_stereo = 0; /* "ZERO" */ dspk->mono_to_stereo = 0; /* "Zero" */
dev_set_drvdata(dev, dspk); dev_set_drvdata(dev, dspk);

View File

@@ -2,7 +2,7 @@
/* /*
* tegra186_dspk.h - Definitions for Tegra186 DSPK driver * 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 rx_fifo_th;
unsigned int osr_val; unsigned int osr_val;
unsigned int lrsel; unsigned int lrsel;
unsigned int srate_override; unsigned int ch_sel;
unsigned int audio_ch_override;
unsigned int ch_sel; /* Used for client channel override */
unsigned int audio_fmt_override;
unsigned int mono_to_stereo; unsigned int mono_to_stereo;
unsigned int stereo_to_mono; unsigned int stereo_to_mono;
struct clk *clk_dspk; struct clk *clk_dspk;