summaryrefslogtreecommitdiff
path: root/sound/soc/fsl
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r--sound/soc/fsl/Kconfig22
-rw-r--r--sound/soc/fsl/Makefile54
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c460
-rw-r--r--sound/soc/fsl/fsl_asrc.c181
-rw-r--r--sound/soc/fsl/fsl_asrc.h2
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h70
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c15
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c729
-rw-r--r--sound/soc/fsl/fsl_aud2htx.c18
-rw-r--r--sound/soc/fsl/fsl_audmix.c46
-rw-r--r--sound/soc/fsl/fsl_dma.c2
-rw-r--r--sound/soc/fsl/fsl_easrc.c275
-rw-r--r--sound/soc/fsl/fsl_easrc.h4
-rw-r--r--sound/soc/fsl/fsl_esai.c15
-rw-r--r--sound/soc/fsl/fsl_micfil.c309
-rw-r--r--sound/soc/fsl/fsl_micfil.h5
-rw-r--r--sound/soc/fsl/fsl_mqs.c128
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c592
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c91
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h2
-rw-r--r--sound/soc/fsl/fsl_sai.c249
-rw-r--r--sound/soc/fsl/fsl_sai.h14
-rw-r--r--sound/soc/fsl/fsl_spdif.c15
-rw-r--r--sound/soc/fsl/fsl_ssi.c12
-rw-r--r--sound/soc/fsl/fsl_utils.c45
-rw-r--r--sound/soc/fsl/fsl_utils.h5
-rw-r--r--sound/soc/fsl/fsl_xcvr.c634
-rw-r--r--sound/soc/fsl/fsl_xcvr.h109
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c21
-rw-r--r--sound/soc/fsl/imx-audmix.c111
-rw-r--r--sound/soc/fsl/imx-audmux.c10
-rw-r--r--sound/soc/fsl/imx-card.c97
-rw-r--r--sound/soc/fsl/imx-es8328.c18
-rw-r--r--sound/soc/fsl/imx-hdmi.c2
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c1
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c4
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c60
-rw-r--r--sound/soc/fsl/imx-rpmsg.c32
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c2
-rw-r--r--sound/soc/fsl/imx-spdif.c103
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.c372
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.h80
-rw-r--r--sound/soc/fsl/lpc3xxx-pcm.c72
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c2
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c4
-rw-r--r--sound/soc/fsl/p1022_ds.c2
-rw-r--r--sound/soc/fsl/p1022_rdk.c2
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c2
48 files changed, 4086 insertions, 1014 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 270726c134b3..c4cf3cff58de 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -6,8 +6,11 @@ comment "Common SoC Audio options for Freescale CPUs:"
config SND_SOC_FSL_ASRC
tristate "Asynchronous Sample Rate Converter (ASRC) module support"
depends on HAS_DMA
+ select DMA_SHARED_BUFFER
select REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_COMPRESS_ACCEL
+ select SND_COMPRESS_OFFLOAD
help
Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
support for the Freescale CPUs.
@@ -29,6 +32,7 @@ config SND_SOC_FSL_SAI
config SND_SOC_FSL_MQS
tristate "Medium Quality Sound (MQS) module support"
depends on SND_SOC_FSL_SAI
+ depends on IMX_SCMI_MISC_DRV || !IMX_SCMI_MISC_DRV
select REGMAP_MMIO
help
Say Y if you want to add Medium Quality Sound (MQS)
@@ -103,6 +107,7 @@ config SND_SOC_FSL_XCVR
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_GENERIC_DMAENGINE_PCM
+ select SND_SOC_FSL_UTILS
help
Say Y if you want to add Audio Transceiver (XCVR) support for NXP
iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC,
@@ -130,6 +135,13 @@ config SND_SOC_FSL_RPMSG
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_LPC3XXX
+ tristate "SoC Audio for NXP LPC32XX CPUs"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the LPC3XXX I2S interface.
+
config SND_SOC_IMX_PCM_DMA
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -295,15 +307,6 @@ config SND_SOC_IMX_SGTL5000
SND_SOC_FSL_ASOC_CARD and SND_SOC_SGTL5000 to use the newer
driver.
-config SND_SOC_IMX_SPDIF
- tristate "SoC Audio support for i.MX boards with S/PDIF"
- select SND_SOC_IMX_PCM_DMA
- select SND_SOC_FSL_SPDIF
- help
- SoC Audio support for i.MX boards with S/PDIF
- Say Y if you want to add support for SoC audio on an i.MX board with
- a S/DPDIF.
-
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
@@ -315,6 +318,7 @@ config SND_SOC_FSL_ASOC_CARD
select SND_SOC_FSL_ESAI
select SND_SOC_FSL_SAI
select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_SPDIF
select SND_SOC_TLV320AIC31XX
select SND_SOC_WM8994
select MFD_WM8994
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b45eda80c196..d656a9ab54e3 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -1,34 +1,36 @@
# SPDX-License-Identifier: GPL-2.0
# P1022 DS Machine Support
-snd-soc-p1022-ds-objs := p1022_ds.o
+snd-soc-p1022-ds-y := p1022_ds.o
obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
# P1022 RDK Machine Support
-snd-soc-p1022-rdk-objs := p1022_rdk.o
+snd-soc-p1022-rdk-y := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
-snd-soc-fsl-audmix-objs := fsl_audmix.o
-snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
-snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
-snd-soc-fsl-sai-objs := fsl_sai.o
+snd-soc-fsl-audmix-y := fsl_audmix.o
+snd-soc-fsl-asoc-card-y := fsl-asoc-card.o
+snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o
+snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o
+snd-soc-fsl-sai-y := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
-snd-soc-fsl-spdif-objs := fsl_spdif.o
-snd-soc-fsl-esai-objs := fsl_esai.o
-snd-soc-fsl-micfil-objs := fsl_micfil.o
-snd-soc-fsl-utils-objs := fsl_utils.o
-snd-soc-fsl-dma-objs := fsl_dma.o
-snd-soc-fsl-mqs-objs := fsl_mqs.o
-snd-soc-fsl-easrc-objs := fsl_easrc.o
-snd-soc-fsl-xcvr-objs := fsl_xcvr.o
-snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o
-snd-soc-fsl-rpmsg-objs := fsl_rpmsg.o
-snd-soc-fsl-qmc-audio-objs := fsl_qmc_audio.o
+snd-soc-fsl-spdif-y := fsl_spdif.o
+snd-soc-fsl-esai-y := fsl_esai.o
+snd-soc-fsl-micfil-y := fsl_micfil.o
+snd-soc-fsl-utils-y := fsl_utils.o
+snd-soc-fsl-dma-y := fsl_dma.o
+snd-soc-fsl-mqs-y := fsl_mqs.o
+snd-soc-fsl-easrc-y := fsl_easrc.o
+snd-soc-fsl-xcvr-y := fsl_xcvr.o
+snd-soc-fsl-aud2htx-y := fsl_aud2htx.o
+snd-soc-fsl-rpmsg-y := fsl_rpmsg.o
+snd-soc-fsl-qmc-audio-y := fsl_qmc_audio.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
+obj-$(CONFIG_SND_SOC_FSL_LPC3XXX) += snd-soc-fsl-lpc3xxx.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
@@ -53,7 +55,7 @@ obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
# i.MX Platform Support
-snd-soc-imx-audmux-objs := imx-audmux.o
+snd-soc-imx-audmux-y := imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
@@ -62,19 +64,17 @@ obj-$(CONFIG_SND_SOC_IMX_AUDIO_RPMSG) += imx-audio-rpmsg.o
obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o
# i.MX Machine Support
-snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
-snd-soc-imx-es8328-objs := imx-es8328.o
-snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
-snd-soc-imx-spdif-objs := imx-spdif.o
-snd-soc-imx-audmix-objs := imx-audmix.o
-snd-soc-imx-hdmi-objs := imx-hdmi.o
-snd-soc-imx-rpmsg-objs := imx-rpmsg.o
-snd-soc-imx-card-objs := imx-card.o
+snd-soc-eukrea-tlv320-y := eukrea-tlv320.o
+snd-soc-imx-es8328-y := imx-es8328.o
+snd-soc-imx-sgtl5000-y := imx-sgtl5000.o
+snd-soc-imx-audmix-y := imx-audmix.o
+snd-soc-imx-hdmi-y := imx-hdmi.o
+snd-soc-imx-rpmsg-y := imx-rpmsg.o
+snd-soc-imx-card-y := imx-card.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
-obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index bc07f26ba303..ab583b432c60 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -28,6 +28,7 @@
#include "../codecs/wm8994.h"
#include "../codecs/tlv320aic31xx.h"
#include "../codecs/nau8822.h"
+#include "../codecs/wm8904.h"
#define DRIVER_NAME "fsl-asoc-card"
@@ -98,7 +99,7 @@ struct fsl_asoc_card_priv {
struct simple_util_jack hp_jack;
struct simple_util_jack mic_jack;
struct platform_device *pdev;
- struct codec_priv codec_priv;
+ struct codec_priv codec_priv[2];
struct cpu_priv cpu_priv;
struct snd_soc_card card;
u8 streams;
@@ -171,10 +172,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- struct codec_priv *codec_priv = &priv->codec_priv;
+ struct codec_priv *codec_priv;
+ struct snd_soc_dai *codec_dai;
struct cpu_priv *cpu_priv = &priv->cpu_priv;
struct device *dev = rtd->card->dev;
unsigned int pll_out;
+ int codec_idx;
int ret;
priv->sample_rate = params_rate(params);
@@ -207,28 +210,32 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
}
/* Specific configuration for PLL */
- if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
- if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
- pll_out = priv->sample_rate * 384;
- else
- pll_out = priv->sample_rate * 256;
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
- ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0),
- codec_priv->pll_id,
- codec_priv->mclk_id,
- codec_priv->mclk_freq, pll_out);
- if (ret) {
- dev_err(dev, "failed to start FLL: %d\n", ret);
- goto fail;
- }
+ if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
+ if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = priv->sample_rate * 384;
+ else
+ pll_out = priv->sample_rate * 256;
- ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0),
- codec_priv->fll_id,
- pll_out, SND_SOC_CLOCK_IN);
+ ret = snd_soc_dai_set_pll(codec_dai,
+ codec_priv->pll_id,
+ codec_priv->mclk_id,
+ codec_priv->mclk_freq, pll_out);
+ if (ret) {
+ dev_err(dev, "failed to start FLL: %d\n", ret);
+ goto fail;
+ }
- if (ret && ret != -ENOTSUPP) {
- dev_err(dev, "failed to set SYSCLK: %d\n", ret);
- goto fail;
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ codec_priv->fll_id,
+ pll_out, SND_SOC_CLOCK_IN);
+
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+ goto fail;
+ }
}
}
@@ -241,30 +248,36 @@ fail:
static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct codec_priv *codec_priv = &priv->codec_priv;
+ struct codec_priv *codec_priv;
+ struct snd_soc_dai *codec_dai;
struct device *dev = rtd->card->dev;
+ int codec_idx;
int ret;
priv->streams &= ~BIT(substream->stream);
- if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
- /* Force freq to be free_freq to avoid error message in codec */
- ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0),
- codec_priv->mclk_id,
- codec_priv->free_freq,
- SND_SOC_CLOCK_IN);
- if (ret) {
- dev_err(dev, "failed to switch away from FLL: %d\n", ret);
- return ret;
- }
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
- ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0),
- codec_priv->pll_id, 0, 0, 0);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dev, "failed to stop FLL: %d\n", ret);
- return ret;
+ if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
+ /* Force freq to be free_freq to avoid error message in codec */
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ codec_priv->mclk_id,
+ codec_priv->free_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to switch away from FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai,
+ codec_priv->pll_id, 0, 0, 0);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to stop FLL: %d\n", ret);
+ return ret;
+ }
}
}
@@ -293,46 +306,25 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-SND_SOC_DAILINK_DEFS(hifi,
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(hifi_fe,
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_DUMMY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-SND_SOC_DAILINK_DEFS(hifi_be,
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
- DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
static const struct snd_soc_dai_link fsl_asoc_card_dai[] = {
/* Default ASoC DAI Link*/
{
.name = "HiFi",
.stream_name = "HiFi",
.ops = &fsl_asoc_card_ops,
- SND_SOC_DAILINK_REG(hifi),
},
/* DPCM Link between Front-End and Back-End (Optional) */
{
.name = "HiFi-ASRC-FE",
.stream_name = "HiFi-ASRC-FE",
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.dynamic = 1,
- SND_SOC_DAILINK_REG(hifi_fe),
},
{
.name = "HiFi-ASRC-BE",
.stream_name = "HiFi-ASRC-BE",
.be_hw_params_fixup = be_hw_params_fixup,
.ops = &fsl_asoc_card_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.no_pcm = 1,
- SND_SOC_DAILINK_REG(hifi_be),
},
};
@@ -464,6 +456,75 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
return 0;
}
+static int fsl_asoc_card_spdif_init(struct device_node *codec_np[],
+ struct device_node *cpu_np,
+ const char *codec_dai_name[],
+ struct fsl_asoc_card_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (!of_node_name_eq(cpu_np, "spdif")) {
+ dev_err(dev, "CPU phandle invalid, should be an SPDIF device\n");
+ return -EINVAL;
+ }
+
+ priv->dai_link[0].playback_only = true;
+ priv->dai_link[0].capture_only = true;
+
+ for (int i = 0; i < 2; i++) {
+ if (!codec_np[i])
+ break;
+
+ if (of_device_is_compatible(codec_np[i], "linux,spdif-dit")) {
+ priv->dai_link[0].capture_only = false;
+ codec_dai_name[i] = "dit-hifi";
+ } else if (of_device_is_compatible(codec_np[i], "linux,spdif-dir")) {
+ priv->dai_link[0].playback_only = false;
+ codec_dai_name[i] = "dir-hifi";
+ }
+ }
+
+ // Old SPDIF DT binding
+ if (!codec_np[0]) {
+ codec_dai_name[0] = snd_soc_dummy_dlc.dai_name;
+ if (of_property_read_bool(np, "spdif-out"))
+ priv->dai_link[0].capture_only = false;
+ if (of_property_read_bool(np, "spdif-in"))
+ priv->dai_link[0].playback_only = false;
+ }
+
+ if (priv->dai_link[0].playback_only && priv->dai_link[0].capture_only) {
+ dev_err(dev, "no enabled S/PDIF DAI link\n");
+ return -EINVAL;
+ }
+
+ if (priv->dai_link[0].playback_only) {
+ priv->dai_link[1].playback_only = true;
+ priv->dai_link[2].playback_only = true;
+ priv->card.dapm_routes = audio_map_tx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
+ } else if (priv->dai_link[0].capture_only) {
+ priv->dai_link[1].capture_only = true;
+ priv->dai_link[2].capture_only = true;
+ priv->card.dapm_routes = audio_map_rx;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
+ }
+
+ // No DAPM routes with old bindings and dummy codec
+ if (!codec_np[0]) {
+ priv->card.dapm_routes = NULL;
+ priv->card.num_dapm_routes = 0;
+ }
+
+ if (codec_np[0] && codec_np[1]) {
+ priv->dai_link[0].num_codecs = 2;
+ priv->dai_link[2].num_codecs = 2;
+ }
+
+ return 0;
+}
+
static int hp_jack_event(struct notifier_block *nb, unsigned long event,
void *data)
{
@@ -503,9 +564,10 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_pcm_runtime *rtd = list_first_entry(
&card->rtd_list, struct snd_soc_pcm_runtime, list);
- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- struct codec_priv *codec_priv = &priv->codec_priv;
+ struct snd_soc_dai *codec_dai;
+ struct codec_priv *codec_priv;
struct device *dev = card->dev;
+ int codec_idx;
int ret;
if (fsl_asoc_card_is_ac97(priv)) {
@@ -525,32 +587,40 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
return 0;
}
- ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
- codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dev, "failed to set sysclk in %s\n", __func__);
- return ret;
- }
+ for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
+ codec_priv = &priv->codec_priv[codec_idx];
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+ codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+ return ret;
+ }
- if (!IS_ERR_OR_NULL(codec_priv->mclk))
- clk_prepare_enable(codec_priv->mclk);
+ if (!IS_ERR_OR_NULL(codec_priv->mclk))
+ clk_prepare_enable(codec_priv->mclk);
+ }
return 0;
}
static int fsl_asoc_card_probe(struct platform_device *pdev)
{
- struct device_node *cpu_np, *codec_np, *asrc_np;
+ struct device_node *cpu_np, *asrc_np;
+ struct snd_soc_dai_link_component *codec_comp;
+ struct device_node *codec_np[2];
struct device_node *np = pdev->dev.of_node;
struct platform_device *asrc_pdev = NULL;
struct device_node *bitclkprovider = NULL;
struct device_node *frameprovider = NULL;
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
- struct device *codec_dev = NULL;
- const char *codec_dai_name;
- const char *codec_dev_name;
+ struct device *codec_dev[2] = { NULL, NULL };
+ struct snd_soc_dai_link_component *dlc;
+ const char *codec_dai_name[2];
+ const char *codec_dev_name[2];
u32 asrc_fmt = 0;
+ int codec_idx;
u32 width;
int ret;
@@ -558,10 +628,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ priv->pdev = pdev;
+
cpu_np = of_parse_phandle(np, "audio-cpu", 0);
- /* Give a chance to old DT binding */
+ /* Give a chance to old DT bindings */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
+ if (!cpu_np)
+ cpu_np = of_parse_phandle(np, "spdif-controller", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
ret = -EINVAL;
@@ -575,21 +649,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
- codec_np = of_parse_phandle(np, "audio-codec", 0);
- if (codec_np) {
- struct platform_device *codec_pdev;
- struct i2c_client *codec_i2c;
+ codec_np[0] = of_parse_phandle(np, "audio-codec", 0);
+ codec_np[1] = of_parse_phandle(np, "audio-codec", 1);
- codec_i2c = of_find_i2c_device_by_node(codec_np);
- if (codec_i2c) {
- codec_dev = &codec_i2c->dev;
- codec_dev_name = codec_i2c->name;
- }
- if (!codec_dev) {
- codec_pdev = of_find_device_by_node(codec_np);
- if (codec_pdev) {
- codec_dev = &codec_pdev->dev;
- codec_dev_name = codec_pdev->name;
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ if (codec_np[codec_idx]) {
+ struct platform_device *codec_pdev;
+ struct i2c_client *codec_i2c;
+
+ codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]);
+ if (codec_i2c) {
+ codec_dev[codec_idx] = &codec_i2c->dev;
+ codec_dev_name[codec_idx] = codec_i2c->name;
+ }
+ if (!codec_dev[codec_idx]) {
+ codec_pdev = of_find_device_by_node(codec_np[codec_idx]);
+ if (codec_pdev) {
+ codec_dev[codec_idx] = &codec_pdev->dev;
+ codec_dev_name[codec_idx] = codec_pdev->name;
+ }
}
}
}
@@ -599,12 +677,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
- if (codec_dev) {
- struct clk *codec_clk = clk_get(codec_dev, NULL);
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ if (codec_dev[codec_idx]) {
+ struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL);
- if (!IS_ERR(codec_clk)) {
- priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
- clk_put(codec_clk);
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
}
}
@@ -617,98 +697,140 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
memcpy(priv->dai_link, fsl_asoc_card_dai,
sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+ /*
+ * "Default ASoC DAI Link": 1 cpus, 2 codecs, 1 platforms
+ * "DPCM Link Front-End": 1 cpus, 1 codecs (dummy), 1 platforms
+ * "DPCM Link Back-End": 1 cpus, 2 codecs
+ * totally 10 components
+ */
+ dlc = devm_kcalloc(&pdev->dev, 10, sizeof(*dlc), GFP_KERNEL);
+ if (!dlc) {
+ ret = -ENOMEM;
+ goto asrc_fail;
+ }
+
+ priv->dai_link[0].cpus = &dlc[0];
+ priv->dai_link[0].num_cpus = 1;
+ priv->dai_link[0].codecs = &dlc[1];
+ priv->dai_link[0].num_codecs = 1;
+ priv->dai_link[0].platforms = &dlc[3];
+ priv->dai_link[0].num_platforms = 1;
+
+ priv->dai_link[1].cpus = &dlc[4];
+ priv->dai_link[1].num_cpus = 1;
+ priv->dai_link[1].codecs = &dlc[5];
+ priv->dai_link[1].num_codecs = 0; /* dummy */
+ priv->dai_link[1].platforms = &dlc[6];
+ priv->dai_link[1].num_platforms = 1;
+
+ priv->dai_link[2].cpus = &dlc[7];
+ priv->dai_link[2].num_cpus = 1;
+ priv->dai_link[2].codecs = &dlc[8];
+ priv->dai_link[2].num_codecs = 1;
priv->card.dapm_routes = audio_map;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.driver_name = DRIVER_NAME;
- priv->codec_priv.fll_id = -1;
- priv->codec_priv.pll_id = -1;
+ for (codec_idx = 0; codec_idx < 2; codec_idx++) {
+ priv->codec_priv[codec_idx].fll_id = -1;
+ priv->codec_priv[codec_idx].pll_id = -1;
+ }
/* Diversify the card configurations */
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
- codec_dai_name = "cs42888";
- priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
- priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
+ codec_dai_name[0] = "cs42888";
+ priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq;
+ priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.slot_width = 32;
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
} else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
- codec_dai_name = "cs4271-hifi";
- priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK;
+ codec_dai_name[0] = "cs4271-hifi";
+ priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
- codec_dai_name = "sgtl5000";
- priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
+ codec_dai_name[0] = "sgtl5000";
+ priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) {
- codec_dai_name = "tlv320aic32x4-hifi";
+ codec_dai_name[0] = "tlv320aic32x4-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) {
- codec_dai_name = "tlv320dac31xx-hifi";
- priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
- priv->dai_link[1].dpcm_capture = 0;
- priv->dai_link[2].dpcm_capture = 0;
+ codec_dai_name[0] = "tlv320dac31xx-hifi";
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
- codec_dai_name = "wm8962";
- priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
- priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
- priv->codec_priv.pll_id = WM8962_FLL;
+ codec_dai_name[0] = "wm8962";
+ priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK;
+ priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL;
+ priv->codec_priv[0].pll_id = WM8962_FLL;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
- codec_dai_name = "wm8960-hifi";
- priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
- priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
+ codec_dai_name[0] = "wm8960-hifi";
+ priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO;
+ priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
- codec_dai_name = "ac97-hifi";
+ codec_dai_name[0] = "ac97-hifi";
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
priv->card.dapm_routes = audio_map_ac97;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97);
} else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) {
- codec_dai_name = "fsl-mqs-dai";
+ codec_dai_name[0] = "fsl-mqs-dai";
priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J |
SND_SOC_DAIFMT_CBC_CFC |
SND_SOC_DAIFMT_NB_NF;
- priv->dai_link[1].dpcm_capture = 0;
- priv->dai_link[2].dpcm_capture = 0;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) {
- codec_dai_name = "wm8524-hifi";
+ codec_dai_name[0] = "wm8524-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
- priv->dai_link[1].dpcm_capture = 0;
- priv->dai_link[2].dpcm_capture = 0;
+ priv->dai_link[1].playback_only = 1;
+ priv->dai_link[2].playback_only = 1;
priv->cpu_priv.slot_width = 32;
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) {
- codec_dai_name = "si476x-codec";
+ codec_dai_name[0] = "si476x-codec";
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
priv->card.dapm_routes = audio_map_rx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) {
- codec_dai_name = "wm8994-aif1";
+ codec_dai_name[0] = "wm8994-aif1";
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
- priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1;
- priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1;
- priv->codec_priv.pll_id = WM8994_FLL1;
- priv->codec_priv.free_freq = priv->codec_priv.mclk_freq;
+ priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1;
+ priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1;
+ priv->codec_priv[0].pll_id = WM8994_FLL1;
+ priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq;
priv->card.dapm_routes = NULL;
priv->card.num_dapm_routes = 0;
} else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) {
- codec_dai_name = "nau8822-hifi";
- priv->codec_priv.mclk_id = NAU8822_CLK_MCLK;
- priv->codec_priv.fll_id = NAU8822_CLK_PLL;
- priv->codec_priv.pll_id = NAU8822_CLK_PLL;
- priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
- if (codec_dev)
- priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL);
+ codec_dai_name[0] = "nau8822-hifi";
+ priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK;
+ priv->codec_priv[0].fll_id = NAU8822_CLK_PLL;
+ priv->codec_priv[0].pll_id = NAU8822_CLK_PLL;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ if (codec_dev[0])
+ priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL);
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) {
+ codec_dai_name[0] = "wm8904-hifi";
+ priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK;
+ priv->codec_priv[0].fll_id = WM8904_CLK_FLL;
+ priv->codec_priv[0].pll_id = WM8904_FLL_MCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) {
+ ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv);
+ if (ret)
+ goto asrc_fail;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
ret = -EINVAL;
@@ -719,18 +841,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
* Allow setting mclk-id from the device-tree node. Otherwise, the
* default value for each card configuration is used.
*/
- of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id);
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ of_property_read_u32_index(np, "mclk-id", codec_idx,
+ &priv->codec_priv[codec_idx].mclk_id);
+ }
/* Format info from DT is optional. */
snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider);
if (bitclkprovider || frameprovider) {
unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL);
+ bool codec_bitclkprovider = false;
+ bool codec_frameprovider = false;
+
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ if (bitclkprovider && codec_np[codec_idx] == bitclkprovider)
+ codec_bitclkprovider = true;
+ if (frameprovider && codec_np[codec_idx] == frameprovider)
+ codec_frameprovider = true;
+ }
- if (codec_np == bitclkprovider)
- daifmt |= (codec_np == frameprovider) ?
+ if (codec_bitclkprovider)
+ daifmt |= (codec_frameprovider) ?
SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC;
else
- daifmt |= (codec_np == frameprovider) ?
+ daifmt |= (codec_frameprovider) ?
SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC;
/* Override dai_fmt with value from DT */
@@ -746,7 +880,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
of_node_put(bitclkprovider);
of_node_put(frameprovider);
- if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+ if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0]
+ && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) {
dev_dbg(&pdev->dev, "failed to find codec device\n");
ret = -EPROBE_DEFER;
goto asrc_fail;
@@ -780,13 +915,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
}
/* Initialize sound card */
- priv->pdev = pdev;
priv->card.dev = &pdev->dev;
priv->card.owner = THIS_MODULE;
ret = snd_soc_of_parse_card_name(&priv->card, "model");
if (ret) {
snprintf(priv->name, sizeof(priv->name), "%s-audio",
- fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name);
+ fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]);
priv->card.name = priv->name;
}
priv->card.dai_link = priv->dai_link;
@@ -798,7 +932,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!asrc_pdev)
priv->card.num_dapm_routes /= 2;
- if (of_property_read_bool(np, "audio-routing")) {
+ if (of_property_present(np, "audio-routing")) {
ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
@@ -808,11 +942,19 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Normal DAI Link */
priv->dai_link[0].cpus->of_node = cpu_np;
- priv->dai_link[0].codecs->dai_name = codec_dai_name;
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ codec_comp->dai_name = codec_dai_name[codec_idx];
+ }
- if (!fsl_asoc_card_is_ac97(priv))
- priv->dai_link[0].codecs->of_node = codec_np;
- else {
+ // Old SPDIF DT binding support
+ if (codec_dai_name[0] == snd_soc_dummy_dlc.dai_name)
+ priv->dai_link[0].codecs[0].name = snd_soc_dummy_dlc.name;
+
+ if (!fsl_asoc_card_is_ac97(priv)) {
+ for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
+ codec_comp->of_node = codec_np[codec_idx];
+ }
+ } else {
u32 idx;
ret = of_property_read_u32(cpu_np, "cell-index", &idx);
@@ -822,11 +964,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto asrc_fail;
}
- priv->dai_link[0].codecs->name =
+ priv->dai_link[0].codecs[0].name =
devm_kasprintf(&pdev->dev, GFP_KERNEL,
"ac97-codec.%u",
(unsigned int)idx);
- if (!priv->dai_link[0].codecs->name) {
+ if (!priv->dai_link[0].codecs[0].name) {
ret = -ENOMEM;
goto asrc_fail;
}
@@ -840,10 +982,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* DPCM DAI Links only if ASRC exists */
priv->dai_link[1].cpus->of_node = asrc_np;
priv->dai_link[1].platforms->of_node = asrc_np;
- priv->dai_link[2].codecs->dai_name = codec_dai_name;
- priv->dai_link[2].codecs->of_node = codec_np;
- priv->dai_link[2].codecs->name =
- priv->dai_link[0].codecs->name;
+ for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) {
+ codec_comp->dai_name = priv->dai_link[0].codecs[codec_idx].dai_name;
+ codec_comp->of_node = priv->dai_link[0].codecs[codec_idx].of_node;
+ codec_comp->name = priv->dai_link[0].codecs[codec_idx].name;
+ }
priv->dai_link[2].cpus->of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
@@ -886,14 +1029,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
}
/*
- * Properties "hp-det-gpio" and "mic-det-gpio" are optional, and
+ * Properties "hp-det-gpios" and "mic-det-gpios" are optional, and
* simple_util_init_jack() uses these properties for creating
* Headphone Jack and Microphone Jack.
*
* The notifier is initialized in snd_soc_card_jack_new(), then
* snd_soc_jack_notifier_register can be called.
*/
- if (of_property_read_bool(np, "hp-det-gpio")) {
+ if (of_property_read_bool(np, "hp-det-gpios") ||
+ of_property_read_bool(np, "hp-det-gpio") /* deprecated */) {
ret = simple_util_init_jack(&priv->card, &priv->hp_jack,
1, NULL, "Headphone Jack");
if (ret)
@@ -902,7 +1046,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
snd_soc_jack_notifier_register(&priv->hp_jack.jack, &hp_jack_nb);
}
- if (of_property_read_bool(np, "mic-det-gpio")) {
+ if (of_property_read_bool(np, "mic-det-gpios") ||
+ of_property_read_bool(np, "mic-det-gpio") /* deprecated */) {
ret = simple_util_init_jack(&priv->card, &priv->mic_jack,
0, NULL, "Mic Jack");
if (ret)
@@ -913,7 +1058,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_fail:
of_node_put(asrc_np);
- of_node_put(codec_np);
+ of_node_put(codec_np[0]);
+ of_node_put(codec_np[1]);
put_device(&cpu_pdev->dev);
fail:
of_node_put(cpu_np);
@@ -935,6 +1081,8 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-si476x", },
{ .compatible = "fsl,imx-audio-wm8958", },
{ .compatible = "fsl,imx-audio-nau8822", },
+ { .compatible = "fsl,imx-audio-wm8904", },
+ { .compatible = "fsl,imx-audio-spdif", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index b793263291dc..677529916dc0 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -1063,6 +1063,139 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_ASRDx(dir, index);
}
+/* Get sample numbers in FIFO */
+static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+
+ regmap_read(asrc->regmap, REG_ASRFST(index), &val);
+
+ val &= ASRFSTi_OUTPUT_FIFO_MASK;
+
+ return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
+}
+
+static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc_pair_priv *pair_priv = pair->private;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ struct asrc_config config;
+ int ret;
+
+ /* fill config */
+ config.pair = pair->index;
+ config.channel_num = pair->channels;
+ config.input_sample_rate = pair->rate[IN];
+ config.output_sample_rate = pair->rate[OUT];
+ config.input_format = pair->sample_format[IN];
+ config.output_format = pair->sample_format[OUT];
+ config.inclk = INCLK_NONE;
+ config.outclk = OUTCLK_ASRCK1_CLK;
+
+ pair_priv->config = &config;
+ ret = fsl_asrc_config_pair(pair, true);
+ if (ret) {
+ dev_err(dev, "failed to config pair: %d\n", ret);
+ return ret;
+ }
+
+ pair->first_convert = 1;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair)
+{
+ if (pair->first_convert) {
+ fsl_asrc_start_pair(pair);
+ pair->first_convert = 0;
+ }
+ /*
+ * Clear DMA request during the stall state of ASRC:
+ * During STALL state, the remaining in input fifo would never be
+ * smaller than the input threshold while the output fifo would not
+ * be bigger than output one. Thus the DMA request would be cleared.
+ */
+ fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
+ ASRC_FIFO_THRESHOLD_MAX);
+
+ /* Update the real input threshold to raise DMA request */
+ fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML,
+ ASRC_M2M_OUTPUTFIFO_WML);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair)
+{
+ if (!pair->first_convert) {
+ fsl_asrc_stop_pair(pair);
+ pair->first_convert = 1;
+ }
+
+ return 0;
+}
+
+/* calculate capture data length according to output data length and sample rate */
+static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
+{
+ unsigned int in_width, out_width;
+ unsigned int channels = pair->channels;
+ unsigned int in_samples, out_samples;
+ unsigned int out_length;
+
+ in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8;
+ out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8;
+
+ in_samples = input_buffer_length / in_width / channels;
+ out_samples = pair->rate[OUT] * in_samples / pair->rate[IN];
+ out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels;
+
+ return out_length;
+}
+
+static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct fsl_asrc_priv *asrc_priv = asrc->private;
+ int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML;
+
+ if (!asrc_priv->soc->use_edma)
+ return wml * pair->channels;
+ else
+ return 1;
+}
+
+static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
+{
+ cap->fmt_in = FSL_ASRC_FORMATS;
+ cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8;
+
+ cap->rate_in = supported_asrc_rate;
+ cap->rate_in_count = ARRAY_SIZE(supported_asrc_rate);
+ cap->rate_out = supported_asrc_rate;
+ cap->rate_out_count = ARRAY_SIZE(supported_asrc_rate);
+ cap->chan_min = 1;
+ cap->chan_max = 10;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ int i;
+
+ for (i = 0; i < pair->channels * 4; i++)
+ regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0);
+
+ pair->first_convert = 1;
+ return 0;
+}
+
static int fsl_asrc_runtime_resume(struct device *dev);
static int fsl_asrc_runtime_suspend(struct device *dev);
@@ -1147,6 +1280,15 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
+ asrc->m2m_prepare = fsl_asrc_m2m_prepare;
+ asrc->m2m_start = fsl_asrc_m2m_start;
+ asrc->m2m_stop = fsl_asrc_m2m_stop;
+ asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size;
+ asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len;
+ asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
+ asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
+ asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
+
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->clk_map[IN] = input_clk_map_imx35;
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
@@ -1242,6 +1384,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
goto err_pm_get_sync;
}
+ ret = fsl_asrc_m2m_init(asrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
+ return ret;
+ }
+
return 0;
err_pm_get_sync:
@@ -1254,6 +1402,10 @@ err_pm_disable:
static void fsl_asrc_remove(struct platform_device *pdev)
{
+ struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev);
+
+ fsl_asrc_m2m_exit(asrc);
+
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
fsl_asrc_runtime_suspend(&pdev->dev);
@@ -1355,10 +1507,29 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
return 0;
}
+static int fsl_asrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ int ret;
+
+ fsl_asrc_m2m_suspend(asrc);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
+}
+
+static int fsl_asrc_resume(struct device *dev)
+{
+ struct fsl_asrc *asrc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ fsl_asrc_m2m_resume(asrc);
+ return ret;
+}
+
static const struct dev_pm_ops fsl_asrc_pm = {
- SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
};
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
@@ -1392,11 +1563,11 @@ MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
static struct platform_driver fsl_asrc_driver = {
.probe = fsl_asrc_probe,
- .remove_new = fsl_asrc_remove,
+ .remove = fsl_asrc_remove,
.driver = {
.name = "fsl-asrc",
.of_match_table = fsl_asrc_ids,
- .pm = &fsl_asrc_pm,
+ .pm = pm_ptr(&fsl_asrc_pm),
},
};
module_platform_driver(fsl_asrc_driver);
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
index 86d2422ad606..1c492eb237f5 100644
--- a/sound/soc/fsl/fsl_asrc.h
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -12,6 +12,8 @@
#include "fsl_asrc_common.h"
+#define ASRC_M2M_INPUTFIFO_WML 0x4
+#define ASRC_M2M_OUTPUTFIFO_WML 0x2
#define ASRC_DMA_BUFFER_NUM 2
#define ASRC_INPUTFIFO_THRESHOLD 32
#define ASRC_OUTPUTFIFO_THRESHOLD 32
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
index 7e1c13ca37f1..0cd595b0f629 100644
--- a/sound/soc/fsl/fsl_asrc_common.h
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -22,6 +22,26 @@ enum asrc_pair_index {
#define PAIR_CTX_NUM 0x4
/**
+ * struct fsl_asrc_m2m_cap - capability data
+ * @fmt_in: input sample format
+ * @fmt_out: output sample format
+ * @chan_min: minimum channel number
+ * @chan_max: maximum channel number
+ * @rate_in: minimum rate
+ * @rate_out: maximum rete
+ */
+struct fsl_asrc_m2m_cap {
+ u64 fmt_in;
+ u64 fmt_out;
+ int chan_min;
+ int chan_max;
+ const unsigned int *rate_in;
+ int rate_in_count;
+ const unsigned int *rate_out;
+ int rate_out_count;
+};
+
+/**
* fsl_asrc_pair: ASRC Pair common data
*
* @asrc: pointer to its parent module
@@ -34,6 +54,14 @@ enum asrc_pair_index {
* @pos: hardware pointer position
* @req_dma_chan: flag to release dev_to_dev chan
* @private: pair private area
+ * @complete: dma task complete
+ * @sample_format: format of m2m
+ * @rate: rate of m2m
+ * @buf_len: buffer length of m2m
+ * @dma_buffer: buffer pointers
+ * @first_convert: start of conversion
+ * @ratio_mod_flag: flag for new ratio modifier
+ * @ratio_mod: ratio modification
*/
struct fsl_asrc_pair {
struct fsl_asrc *asrc;
@@ -49,6 +77,16 @@ struct fsl_asrc_pair {
bool req_dma_chan;
void *private;
+
+ /* used for m2m */
+ struct completion complete[2];
+ snd_pcm_format_t sample_format[2];
+ unsigned int rate[2];
+ unsigned int buf_len[2];
+ struct snd_dma_buffer dma_buffer[2];
+ unsigned int first_convert;
+ bool ratio_mod_flag;
+ unsigned int ratio_mod;
};
/**
@@ -62,6 +100,7 @@ struct fsl_asrc_pair {
* @mem_clk: clock source to access register
* @ipg_clk: clock source to drive peripheral
* @spba_clk: SPBA clock (optional, depending on SoC design)
+ * @card: compress sound card
* @lock: spin lock for resource protection
* @pair: pair pointers
* @channel_avail: non-occupied channel numbers
@@ -72,6 +111,17 @@ struct fsl_asrc_pair {
* @request_pair: function pointer
* @release_pair: function pointer
* @get_fifo_addr: function pointer
+ * @m2m_get_cap: function pointer
+ * @m2m_prepare: function pointer
+ * @m2m_start: function pointer
+ * @m2m_unprepare: function pointer
+ * @m2m_stop: function pointer
+ * @m2m_calc_out_len: function pointer
+ * @m2m_get_maxburst: function pointer
+ * @m2m_pair_suspend: function pointer
+ * @m2m_pair_resume: function pointer
+ * @m2m_set_ratio_mod: function pointer
+ * @get_output_fifo_size: function pointer
* @pair_priv_size: size of pair private struct.
* @private: private data structure
*/
@@ -84,6 +134,7 @@ struct fsl_asrc {
struct clk *mem_clk;
struct clk *ipg_clk;
struct clk *spba_clk;
+ struct snd_card *card;
spinlock_t lock; /* spin lock for resource protection */
struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
@@ -97,6 +148,20 @@ struct fsl_asrc {
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
void (*release_pair)(struct fsl_asrc_pair *pair);
int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
+ int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap);
+
+ int (*m2m_prepare)(struct fsl_asrc_pair *pair);
+ int (*m2m_start)(struct fsl_asrc_pair *pair);
+ int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
+ int (*m2m_stop)(struct fsl_asrc_pair *pair);
+
+ int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
+ int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
+ int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair);
+ int (*m2m_pair_resume)(struct fsl_asrc_pair *pair);
+ int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val);
+
+ unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair);
size_t pair_priv_size;
void *private;
@@ -105,4 +170,9 @@ struct fsl_asrc {
#define DRV_NAME "fsl-asrc-dai"
extern struct snd_soc_component_driver fsl_asrc_component;
+int fsl_asrc_m2m_init(struct fsl_asrc *asrc);
+void fsl_asrc_m2m_exit(struct fsl_asrc *asrc);
+int fsl_asrc_m2m_resume(struct fsl_asrc *asrc);
+int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc);
+
#endif /* _FSL_ASRC_COMMON_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index f501f47242fb..1bba48318e2d 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -156,11 +156,24 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
for_each_dpcm_be(rtd, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
struct snd_pcm_substream *substream_be;
- struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(be, 0);
+ struct snd_soc_dai *dai_cpu = snd_soc_rtd_to_cpu(be, 0);
+ struct snd_soc_dai *dai_codec = snd_soc_rtd_to_codec(be, 0);
+ struct snd_soc_dai *dai;
if (dpcm->fe != rtd)
continue;
+ /*
+ * With audio graph card, original cpu dai is changed to codec
+ * device in backend, so if cpu dai is dummy device in backend,
+ * get the codec dai device, which is the real hardware device
+ * connected.
+ */
+ if (!snd_soc_dai_is_dummy(dai_cpu))
+ dai = dai_cpu;
+ else
+ dai = dai_codec;
+
substream_be = snd_soc_dpcm_get_substream(be, stream);
dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
dev_be = dai->dev;
diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c
new file mode 100644
index 000000000000..f46881f71e43
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_m2m.c
@@ -0,0 +1,729 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+// Copyright (C) 2019-2024 NXP
+//
+// Freescale ASRC Memory to Memory (M2M) driver
+
+#include <linux/dma/imx-dma.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <sound/asound.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+
+#include "fsl_asrc_common.h"
+
+#define DIR_STR(dir) (dir) == IN ? "in" : "out"
+
+#define ASRC_xPUT_DMA_CALLBACK(dir) \
+ (((dir) == IN) ? asrc_input_dma_callback \
+ : asrc_output_dma_callback)
+
+/* Maximum output and capture buffer size */
+#define ASRC_M2M_BUFFER_SIZE (512 * 1024)
+
+/* Maximum output and capture period size */
+#define ASRC_M2M_PERIOD_SIZE (48 * 1024)
+
+/* dma complete callback */
+static void asrc_input_dma_callback(void *data)
+{
+ struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
+
+ complete(&pair->complete[IN]);
+}
+
+/* dma complete callback */
+static void asrc_output_dma_callback(void *data)
+{
+ struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
+
+ complete(&pair->complete[OUT]);
+}
+
+/**
+ *asrc_read_last_fifo: read all the remaining data from FIFO
+ *@pair: Structure pointer of fsl_asrc_pair
+ *@dma_vaddr: virtual address of capture buffer
+ *@length: payload length of capture buffer
+ */
+static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 i, reg, size, t_size = 0, width;
+ u32 *reg32 = NULL;
+ u16 *reg16 = NULL;
+ u8 *reg24 = NULL;
+
+ width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
+ if (width == 32)
+ reg32 = dma_vaddr + *length;
+ else if (width == 16)
+ reg16 = dma_vaddr + *length;
+ else
+ reg24 = dma_vaddr + *length;
+retry:
+ size = asrc->get_output_fifo_size(pair);
+ if (size + *length > ASRC_M2M_BUFFER_SIZE)
+ goto end;
+
+ for (i = 0; i < size * pair->channels; i++) {
+ regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), &reg);
+ if (reg32) {
+ *reg32++ = reg;
+ } else if (reg16) {
+ *reg16++ = (u16)reg;
+ } else {
+ *reg24++ = (u8)reg;
+ *reg24++ = (u8)(reg >> 8);
+ *reg24++ = (u8)(reg >> 16);
+ }
+ }
+ t_size += size;
+
+ /* In case there is data left in FIFO */
+ if (size)
+ goto retry;
+end:
+ /* Update payload length */
+ if (reg32)
+ *length += t_size * pair->channels * 4;
+ else if (reg16)
+ *length += t_size * pair->channels * 2;
+ else
+ *length += t_size * pair->channels * 3;
+}
+
+/* config dma channel */
+static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
+ struct dma_chan *chan,
+ u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
+ int dir, int width)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ struct dma_slave_config slave_config;
+ enum dma_slave_buswidth buswidth;
+ unsigned int sg_len, max_period_size;
+ struct scatterlist *sg;
+ int ret, i;
+
+ switch (width) {
+ case 8:
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case 16:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case 24:
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ break;
+ case 32:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ dev_err(dev, "invalid word width\n");
+ return -EINVAL;
+ }
+
+ memset(&slave_config, 0, sizeof(slave_config));
+ if (dir == IN) {
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = dma_addr;
+ slave_config.dst_addr_width = buswidth;
+ slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
+ } else {
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = dma_addr;
+ slave_config.src_addr_width = buswidth;
+ slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
+ }
+
+ ret = dmaengine_slave_config(chan, &slave_config);
+ if (ret) {
+ dev_err(dev, "failed to config dmaengine for %s task: %d\n",
+ DIR_STR(dir), ret);
+ return -EINVAL;
+ }
+
+ max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
+ /* scatter gather mode */
+ sg_len = buf_len / max_period_size;
+ if (buf_len % max_period_size)
+ sg_len += 1;
+
+ sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
+ if (!sg)
+ return -ENOMEM;
+
+ sg_init_table(sg, sg_len);
+ for (i = 0; i < (sg_len - 1); i++) {
+ sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
+ sg_dma_len(&sg[i]) = max_period_size;
+ }
+ sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
+ sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
+
+ pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
+ slave_config.direction,
+ DMA_PREP_INTERRUPT);
+ kfree(sg);
+ if (!pair->desc[dir]) {
+ dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
+ return -EINVAL;
+ }
+
+ pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
+ pair->desc[dir]->callback_param = pair;
+
+ return 0;
+}
+
+/* main function of converter */
+static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ enum asrc_pair_index index = pair->index;
+ struct snd_dma_buffer *src_buf, *dst_buf;
+ unsigned int in_buf_len;
+ unsigned int out_dma_len;
+ unsigned int width;
+ u32 fifo_addr;
+ int ret = 0;
+
+ /* set ratio mod */
+ if (asrc->m2m_set_ratio_mod) {
+ if (pair->ratio_mod_flag) {
+ asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
+ pair->ratio_mod_flag = false;
+ }
+ }
+
+ src_buf = &pair->dma_buffer[IN];
+ dst_buf = &pair->dma_buffer[OUT];
+
+ width = snd_pcm_format_physical_width(pair->sample_format[IN]);
+ fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
+
+ in_buf_len = task->input_size;
+
+ if (in_buf_len < width * pair->channels / 8 ||
+ in_buf_len > ASRC_M2M_BUFFER_SIZE ||
+ in_buf_len % (width * pair->channels / 8)) {
+ dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* dma config for output dma channel */
+ ret = asrc_dmaconfig(pair,
+ pair->dma_chan[IN],
+ fifo_addr,
+ src_buf->addr,
+ in_buf_len, IN, width);
+ if (ret) {
+ dev_err(dev, "out dma config error\n");
+ goto end;
+ }
+
+ width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
+ fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
+ out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
+ if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
+ /* dma config for capture dma channel */
+ ret = asrc_dmaconfig(pair,
+ pair->dma_chan[OUT],
+ fifo_addr,
+ dst_buf->addr,
+ out_dma_len, OUT, width);
+ if (ret) {
+ dev_err(dev, "cap dma config error\n");
+ goto end;
+ }
+ } else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
+ dev_err(dev, "cap buffer size error\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ reinit_completion(&pair->complete[IN]);
+ reinit_completion(&pair->complete[OUT]);
+
+ /* Submit DMA request */
+ dmaengine_submit(pair->desc[IN]);
+ dma_async_issue_pending(pair->desc[IN]->chan);
+ if (out_dma_len > 0) {
+ dmaengine_submit(pair->desc[OUT]);
+ dma_async_issue_pending(pair->desc[OUT]->chan);
+ }
+
+ asrc->m2m_start(pair);
+
+ if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
+ dev_err(dev, "out DMA task timeout\n");
+ ret = -ETIMEDOUT;
+ goto end;
+ }
+
+ if (out_dma_len > 0) {
+ if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
+ dev_err(dev, "cap DMA task timeout\n");
+ ret = -ETIMEDOUT;
+ goto end;
+ }
+ }
+
+ /* read the last words from FIFO */
+ asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
+ /* update payload length for capture */
+ task->output_size = out_dma_len;
+end:
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct device *dev = &asrc->pdev->dev;
+ struct fsl_asrc_pair *pair;
+ int size, ret;
+
+ pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
+ if (!pair)
+ return -ENOMEM;
+
+ pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
+ pair->asrc = asrc;
+
+ init_completion(&pair->complete[IN]);
+ init_completion(&pair->complete[OUT]);
+
+ runtime->private_data = pair;
+
+ size = ASRC_M2M_BUFFER_SIZE;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
+ if (ret)
+ goto error_alloc_in_buf;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
+ if (ret)
+ goto error_alloc_out_buf;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to power up asrc\n");
+ goto err_pm_runtime;
+ }
+
+ return 0;
+
+err_pm_runtime:
+ snd_dma_free_pages(&pair->dma_buffer[OUT]);
+error_alloc_out_buf:
+ snd_dma_free_pages(&pair->dma_buffer[IN]);
+error_alloc_in_buf:
+ kfree(pair);
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = &asrc->pdev->dev;
+
+ pm_runtime_put_sync(dev);
+
+ snd_dma_free_pages(&pair->dma_buffer[IN]);
+ snd_dma_free_pages(&pair->dma_buffer[OUT]);
+
+ kfree(runtime->private_data);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc_m2m_cap cap;
+ int ret, i;
+
+ ret = asrc->m2m_get_cap(&cap);
+ if (ret)
+ return -EINVAL;
+
+ if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in)
+ pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format;
+ else
+ return -EINVAL;
+
+ if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out)
+ pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format;
+ else
+ return -EINVAL;
+
+ /* check input rate is in scope */
+ for (i = 0; i < cap.rate_in_count; i++)
+ if (params->codec.sample_rate == cap.rate_in[i]) {
+ pair->rate[IN] = params->codec.sample_rate;
+ break;
+ }
+ if (i == cap.rate_in_count)
+ return -EINVAL;
+
+ /* check output rate is in scope */
+ for (i = 0; i < cap.rate_out_count; i++)
+ if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
+ pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
+ break;
+ }
+ if (i == cap.rate_out_count)
+ return -EINVAL;
+
+ if (params->codec.ch_in != params->codec.ch_out ||
+ params->codec.ch_in < cap.chan_min ||
+ params->codec.ch_in > cap.chan_max)
+ return -EINVAL;
+
+ pair->channels = params->codec.ch_in;
+ pair->buf_len[IN] = params->buffer.fragment_size;
+ pair->buf_len[OUT] = params->buffer.fragment_size;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct snd_dma_buffer *dmab = dmabuf->priv;
+
+ return snd_dma_buffer_mmap(dmab, vma);
+}
+
+static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
+ struct sg_table *sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return NULL;
+
+ if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
+ goto free;
+
+ if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
+ goto free;
+
+ return sgt;
+
+free:
+ sg_free_table(sgt);
+ kfree(sgt);
+ return NULL;
+}
+
+static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+ dma_unmap_sgtable(attachment->dev, table, direction, 0);
+}
+
+static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
+{
+ /* buffer is released by fsl_asrc_m2m_comp_release() */
+}
+
+static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
+ .mmap = fsl_asrc_m2m_mmap,
+ .map_dma_buf = fsl_asrc_m2m_map_dma_buf,
+ .unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
+ .release = fsl_asrc_m2m_release,
+};
+
+static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = &asrc->pdev->dev;
+ int ret;
+
+ exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
+ exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
+ exp_info_in.flags = O_RDWR;
+ exp_info_in.priv = &pair->dma_buffer[IN];
+ task->input = dma_buf_export(&exp_info_in);
+ if (IS_ERR(task->input)) {
+ ret = PTR_ERR(task->input);
+ return ret;
+ }
+
+ exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
+ exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
+ exp_info_out.flags = O_RDWR;
+ exp_info_out.priv = &pair->dma_buffer[OUT];
+ task->output = dma_buf_export(&exp_info_out);
+ if (IS_ERR(task->output)) {
+ ret = PTR_ERR(task->output);
+ return ret;
+ }
+
+ /* Request asrc pair/context */
+ ret = asrc->request_pair(pair->channels, pair);
+ if (ret) {
+ dev_err(dev, "failed to request pair: %d\n", ret);
+ goto err_request_pair;
+ }
+
+ ret = asrc->m2m_prepare(pair);
+ if (ret) {
+ dev_err(dev, "failed to start pair part one: %d\n", ret);
+ goto err_start_part_one;
+ }
+
+ /* Request dma channels */
+ pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
+ if (!pair->dma_chan[IN]) {
+ dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
+ ret = -EBUSY;
+ goto err_dma_channel_in;
+ }
+
+ pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
+ if (!pair->dma_chan[OUT]) {
+ dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
+ ret = -EBUSY;
+ goto err_dma_channel_out;
+ }
+
+ return 0;
+
+err_dma_channel_out:
+ dma_release_channel(pair->dma_chan[IN]);
+err_dma_channel_in:
+ if (asrc->m2m_unprepare)
+ asrc->m2m_unprepare(pair);
+err_start_part_one:
+ asrc->release_pair(pair);
+err_request_pair:
+ return ret;
+}
+
+static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ return asrc_m2m_device_run(pair, task);
+}
+
+static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ return 0;
+}
+
+static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ /* Stop & release pair/context */
+ if (asrc->m2m_stop)
+ asrc->m2m_stop(pair);
+
+ if (asrc->m2m_unprepare)
+ asrc->m2m_unprepare(pair);
+ asrc->release_pair(pair);
+
+ /* Release dma channel */
+ if (pair->dma_chan[IN])
+ dma_release_channel(pair->dma_chan[IN]);
+ if (pair->dma_chan[OUT])
+ dma_release_channel(pair->dma_chan[OUT]);
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ caps->num_codecs = 1;
+ caps->min_fragment_size = 4096;
+ caps->max_fragment_size = 4096;
+ caps->min_fragments = 1;
+ caps->max_fragments = 1;
+ caps->codecs[0] = SND_AUDIOCODEC_PCM;
+
+ return 0;
+}
+
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
+ struct snd_compr_codec_caps *codec)
+{
+ struct fsl_asrc_m2m_cap cap;
+ snd_pcm_format_t k;
+ int j = 0;
+ int ret;
+
+ ret = asrc->m2m_get_cap(&cap);
+ if (ret)
+ return -EINVAL;
+
+ pcm_for_each_format(k) {
+ if (pcm_format_to_bits(k) & cap.fmt_in) {
+ codec->descriptor[j].max_ch = cap.chan_max;
+ memcpy(codec->descriptor[j].sample_rates,
+ cap.rate_in,
+ cap.rate_in_count * sizeof(__u32));
+ codec->descriptor[j].num_sample_rates = cap.rate_in_count;
+ codec->descriptor[j].formats = (__force __u32)k;
+ codec->descriptor[j].pcm_formats = cap.fmt_out;
+ codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
+ codec->descriptor[j].src.out_sample_rate_max =
+ cap.rate_out[cap.rate_out_count - 1];
+ j++;
+ }
+ }
+
+ codec->codec = SND_AUDIOCODEC_PCM;
+ codec->num_descriptors = j;
+ return 0;
+}
+
+static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct fsl_asrc *asrc = stream->private_data;
+
+ return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
+}
+
+static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
+ .open = fsl_asrc_m2m_comp_open,
+ .free = fsl_asrc_m2m_comp_release,
+ .set_params = fsl_asrc_m2m_comp_set_params,
+ .get_caps = fsl_asrc_m2m_get_caps,
+ .get_codec_caps = fsl_asrc_m2m_get_codec_caps,
+ .task_create = fsl_asrc_m2m_comp_task_create,
+ .task_start = fsl_asrc_m2m_comp_task_start,
+ .task_stop = fsl_asrc_m2m_comp_task_stop,
+ .task_free = fsl_asrc_m2m_comp_task_free,
+};
+
+int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
+{
+ struct fsl_asrc_pair *pair;
+ int i;
+
+ for (i = 0; i < PAIR_CTX_NUM; i++) {
+ pair = asrc->pair[i];
+ if (!pair || !pair->dma_buffer[IN].area || !pair->dma_buffer[OUT].area)
+ continue;
+ if (!completion_done(&pair->complete[IN])) {
+ if (pair->dma_chan[IN])
+ dmaengine_terminate_all(pair->dma_chan[IN]);
+ asrc_input_dma_callback((void *)pair);
+ }
+ if (!completion_done(&pair->complete[OUT])) {
+ if (pair->dma_chan[OUT])
+ dmaengine_terminate_all(pair->dma_chan[OUT]);
+ asrc_output_dma_callback((void *)pair);
+ }
+
+ if (asrc->m2m_pair_suspend)
+ asrc->m2m_pair_suspend(pair);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
+
+int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
+{
+ struct fsl_asrc_pair *pair;
+ int i;
+
+ for (i = 0; i < PAIR_CTX_NUM; i++) {
+ pair = asrc->pair[i];
+ if (!pair)
+ continue;
+ if (asrc->m2m_pair_resume)
+ asrc->m2m_pair_resume(pair);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
+
+int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
+{
+ struct device *dev = &asrc->pdev->dev;
+ struct snd_card *card;
+ struct snd_compr *compr;
+ int ret;
+
+ ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (ret < 0)
+ return ret;
+
+ strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
+ strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
+ strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
+
+ asrc->card = card;
+
+ compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
+ if (!compr) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ compr->ops = &fsl_asrc_m2m_compr_ops;
+ compr->private_data = asrc;
+
+ ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
+ if (ret < 0)
+ goto err;
+
+ ret = snd_card_register(card);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ snd_card_free(card);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
+
+void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
+{
+ struct snd_card *card = asrc->card;
+
+ snd_card_free(card);
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
+
+MODULE_IMPORT_NS("DMA_BUF");
+MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
+MODULE_DESCRIPTION("Freescale ASRC M2M driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c
index ee2f6ad1f800..da401561e2de 100644
--- a/sound/soc/fsl/fsl_aud2htx.c
+++ b/sound/soc/fsl/fsl_aud2htx.c
@@ -169,7 +169,7 @@ static const struct regmap_config fsl_aud2htx_regmap_config = {
.readable_reg = fsl_aud2htx_readable_reg,
.volatile_reg = fsl_aud2htx_volatile_reg,
.writeable_reg = fsl_aud2htx_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static const struct of_device_id fsl_aud2htx_dt_ids[] = {
@@ -261,7 +261,7 @@ static void fsl_aud2htx_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
+static int fsl_aud2htx_runtime_suspend(struct device *dev)
{
struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
@@ -271,7 +271,7 @@ static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev)
+static int fsl_aud2htx_runtime_resume(struct device *dev)
{
struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev);
int ret;
@@ -288,19 +288,17 @@ static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops fsl_aud2htx_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend,
- fsl_aud2htx_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume,
+ NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_aud2htx_driver = {
.probe = fsl_aud2htx_probe,
- .remove_new = fsl_aud2htx_remove,
+ .remove = fsl_aud2htx_remove,
.driver = {
.name = "fsl-aud2htx",
- .pm = &fsl_aud2htx_pm_ops,
+ .pm = pm_ptr(&fsl_aud2htx_pm_ops),
.of_match_table = fsl_aud2htx_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index 0ab2c1962117..7981d598ba13 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -326,15 +326,6 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
- .capture = {
- .stream_name = "AUDMIX-Capture-0",
- .channels_min = 8,
- .channels_max = 8,
- .rate_min = 8000,
- .rate_max = 96000,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = FSL_AUDMIX_FORMATS,
- },
.ops = &fsl_audmix_dai_ops,
},
{
@@ -349,8 +340,13 @@ static struct snd_soc_dai_driver fsl_audmix_dai[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_AUDMIX_FORMATS,
},
+ .ops = &fsl_audmix_dai_ops,
+ },
+ {
+ .id = 2,
+ .name = "audmix-2",
.capture = {
- .stream_name = "AUDMIX-Capture-1",
+ .stream_name = "AUDMIX-Capture-0",
.channels_min = 8,
.channels_max = 8,
.rate_min = 8000,
@@ -492,11 +488,17 @@ static int fsl_audmix_probe(struct platform_device *pdev)
goto err_disable_pm;
}
- priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0);
- if (IS_ERR(priv->pdev)) {
- ret = PTR_ERR(priv->pdev);
- dev_err(dev, "failed to register platform: %d\n", ret);
- goto err_disable_pm;
+ /*
+ * If dais property exist, then register the imx-audmix card driver.
+ * otherwise, it should be linked by audio graph card.
+ */
+ if (of_find_property(pdev->dev.of_node, "dais", NULL)) {
+ priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0);
+ if (IS_ERR(priv->pdev)) {
+ ret = PTR_ERR(priv->pdev);
+ dev_err(dev, "failed to register platform: %d\n", ret);
+ goto err_disable_pm;
+ }
}
return 0;
@@ -516,7 +518,6 @@ static void fsl_audmix_remove(struct platform_device *pdev)
platform_device_unregister(priv->pdev);
}
-#ifdef CONFIG_PM
static int fsl_audmix_runtime_resume(struct device *dev)
{
struct fsl_audmix *priv = dev_get_drvdata(dev);
@@ -544,23 +545,20 @@ static int fsl_audmix_runtime_suspend(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_audmix_pm = {
- SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend,
- fsl_audmix_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_audmix_runtime_suspend, fsl_audmix_runtime_resume,
+ NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_audmix_driver = {
.probe = fsl_audmix_probe,
- .remove_new = fsl_audmix_remove,
+ .remove = fsl_audmix_remove,
.driver = {
.name = "fsl-audmix",
.of_match_table = fsl_audmix_ids,
- .pm = &fsl_audmix_pm,
+ .pm = pm_ptr(&fsl_audmix_pm),
},
};
module_platform_driver(fsl_audmix_driver);
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index c4bc9395dff7..aca066b5a43c 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -911,7 +911,7 @@ static struct platform_driver fsl_soc_dma_driver = {
.of_match_table = fsl_soc_dma_ids,
},
.probe = fsl_soc_dma_probe,
- .remove_new = fsl_soc_dma_remove,
+ .remove = fsl_soc_dma_remove,
};
module_platform_driver(fsl_soc_dma_driver);
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index ec53bda46a46..f404a39009e1 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -1748,7 +1748,7 @@ static const struct regmap_config fsl_easrc_regmap_config = {
.rd_table = &fsl_easrc_readable_table,
.wr_table = &fsl_easrc_writeable_table,
.volatile_table = &fsl_easrc_volatileable_table,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
#ifdef DEBUG
@@ -1861,6 +1861,224 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_EASRC_FIFO(dir, index);
}
+/* Get sample numbers in FIFO */
+static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc = pair->asrc;
+ enum asrc_pair_index index = pair->index;
+ u32 val;
+
+ regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val);
+ val &= EASRC_SFS_NSGO_MASK;
+
+ return val >> EASRC_SFS_NSGO_SHIFT;
+}
+
+static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *asrc = pair->asrc;
+ struct device *dev = &asrc->pdev->dev;
+ int ret;
+
+ ctx_priv->in_params.sample_rate = pair->rate[IN];
+ ctx_priv->in_params.sample_format = pair->sample_format[IN];
+ ctx_priv->out_params.sample_rate = pair->rate[OUT];
+ ctx_priv->out_params.sample_format = pair->sample_format[OUT];
+
+ ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
+ ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
+ /* Fill the right half of the re-sampler with zeros */
+ ctx_priv->rs_init_mode = 0x2;
+ /* Zero fill the right half of the prefilter */
+ ctx_priv->pf_init_mode = 0x2;
+
+ ret = fsl_easrc_set_ctx_format(pair,
+ &ctx_priv->in_params.sample_format,
+ &ctx_priv->out_params.sample_format);
+ if (ret) {
+ dev_err(dev, "failed to set context format: %d\n", ret);
+ return ret;
+ }
+
+ ret = fsl_easrc_config_context(asrc, pair->index);
+ if (ret) {
+ dev_err(dev, "failed to config context %d\n", ret);
+ return ret;
+ }
+
+ ctx_priv->in_params.iterations = 1;
+ ctx_priv->in_params.group_len = pair->channels;
+ ctx_priv->in_params.access_len = pair->channels;
+ ctx_priv->out_params.iterations = 1;
+ ctx_priv->out_params.group_len = pair->channels;
+ ctx_priv->out_params.access_len = pair->channels;
+
+ ret = fsl_easrc_set_ctx_organziation(pair);
+ if (ret) {
+ dev_err(dev, "failed to set fifo organization\n");
+ return ret;
+ }
+
+ /* The context start flag */
+ pair->first_convert = 1;
+ return 0;
+}
+
+static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair)
+{
+ /* start context once */
+ if (pair->first_convert) {
+ fsl_easrc_start_context(pair);
+ pair->first_convert = 0;
+ }
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair)
+{
+ /* Stop pair/context */
+ if (!pair->first_convert) {
+ fsl_easrc_stop_context(pair);
+ pair->first_convert = 1;
+ }
+
+ return 0;
+}
+
+/* calculate capture data length according to output data length and sample rate */
+static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
+{
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ unsigned int in_rate = ctx_priv->in_params.norm_rate;
+ unsigned int out_rate = ctx_priv->out_params.norm_rate;
+ unsigned int channels = pair->channels;
+ unsigned int in_samples, out_samples;
+ unsigned int in_width, out_width;
+ unsigned int out_length;
+ unsigned int frac_bits;
+ u64 val1, val2;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val1 = (u64)in_rate << frac_bits;
+ do_div(val1, out_rate);
+ val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31);
+
+ in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8;
+ out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8;
+
+ ctx_priv->in_filled_len += input_buffer_length;
+ if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) {
+ out_length = 0;
+ } else {
+ in_samples = ctx_priv->in_filled_len / (in_width * channels) -
+ ctx_priv->in_filled_sample;
+
+ /* right shift 12 bit to make ratio in 32bit space */
+ val2 = (u64)in_samples << (frac_bits - 12);
+ val1 = val1 >> 12;
+ do_div(val2, val1);
+ out_samples = val2;
+
+ out_length = out_samples * out_width * channels;
+ ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels;
+ }
+
+ return out_length;
+}
+
+static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ if (dir == IN)
+ return ctx_priv->in_params.fifo_wtmk * pair->channels;
+ else
+ return ctx_priv->out_params.fifo_wtmk * pair->channels;
+}
+
+static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair)
+{
+ fsl_easrc_stop_context(pair);
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+
+ pair->first_convert = 1;
+ ctx_priv->in_filled_len = 0;
+
+ return 0;
+}
+
+/* val is Q31 */
+static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val)
+{
+ struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
+ struct fsl_asrc *easrc = pair->asrc;
+ struct fsl_easrc_priv *easrc_priv = easrc->private;
+ unsigned int frac_bits;
+
+ ctx_priv->ratio_mod += val;
+
+ switch (easrc_priv->rs_num_taps) {
+ case EASRC_RS_32_TAPS:
+ /* integer bits = 5; */
+ frac_bits = 39;
+ break;
+ case EASRC_RS_64_TAPS:
+ /* integer bits = 6; */
+ frac_bits = 38;
+ break;
+ case EASRC_RS_128_TAPS:
+ /* integer bits = 7; */
+ frac_bits = 37;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val <<= (frac_bits - 31);
+ regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val));
+
+ return 0;
+}
+
+static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
+{
+ cap->fmt_in = FSL_EASRC_FORMATS;
+ cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ cap->rate_in = easrc_rates;
+ cap->rate_in_count = ARRAY_SIZE(easrc_rates);
+ cap->rate_out = easrc_rates;
+ cap->rate_out_count = ARRAY_SIZE(easrc_rates);
+ cap->chan_min = 1;
+ cap->chan_max = 32;
+ return 0;
+}
+
static const struct of_device_id fsl_easrc_dt_ids[] = {
{ .compatible = "fsl,imx8mn-easrc",},
{}
@@ -1926,6 +2144,16 @@ static int fsl_easrc_probe(struct platform_device *pdev)
easrc->release_pair = fsl_easrc_release_context;
easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv);
+ easrc->m2m_prepare = fsl_easrc_m2m_prepare;
+ easrc->m2m_start = fsl_easrc_m2m_start;
+ easrc->m2m_stop = fsl_easrc_m2m_stop;
+ easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size;
+ easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len;
+ easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst;
+ easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend;
+ easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume;
+ easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod;
+ easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
easrc_priv->const_coeff = 0x3FF0000000000000;
@@ -1976,6 +2204,12 @@ static int fsl_easrc_probe(struct platform_device *pdev)
goto err_pm_disable;
}
+ ret = fsl_asrc_m2m_init(easrc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
+ return ret;
+ }
+
return 0;
err_pm_disable:
@@ -1985,10 +2219,14 @@ err_pm_disable:
static void fsl_easrc_remove(struct platform_device *pdev)
{
+ struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev);
+
+ fsl_asrc_m2m_exit(easrc);
+
pm_runtime_disable(&pdev->dev);
}
-static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev)
+static int fsl_easrc_runtime_suspend(struct device *dev)
{
struct fsl_asrc *easrc = dev_get_drvdata(dev);
struct fsl_easrc_priv *easrc_priv = easrc->private;
@@ -2005,7 +2243,7 @@ static __maybe_unused int fsl_easrc_runtime_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int fsl_easrc_runtime_resume(struct device *dev)
+static int fsl_easrc_runtime_resume(struct device *dev)
{
struct fsl_asrc *easrc = dev_get_drvdata(dev);
struct fsl_easrc_priv *easrc_priv = easrc->private;
@@ -2085,20 +2323,37 @@ disable_mem_clk:
return ret;
}
+static int fsl_easrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ fsl_asrc_m2m_suspend(easrc);
+ ret = pm_runtime_force_suspend(dev);
+ return ret;
+}
+
+static int fsl_easrc_resume(struct device *dev)
+{
+ struct fsl_asrc *easrc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ fsl_asrc_m2m_resume(easrc);
+ return ret;
+}
+
static const struct dev_pm_ops fsl_easrc_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_easrc_runtime_suspend,
- fsl_easrc_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume)
};
static struct platform_driver fsl_easrc_driver = {
.probe = fsl_easrc_probe,
- .remove_new = fsl_easrc_remove,
+ .remove = fsl_easrc_remove,
.driver = {
.name = "fsl-easrc",
- .pm = &fsl_easrc_pm_ops,
+ .pm = pm_ptr(&fsl_easrc_pm_ops),
.of_match_table = fsl_easrc_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h
index 7c70dac52713..c9f770862662 100644
--- a/sound/soc/fsl/fsl_easrc.h
+++ b/sound/soc/fsl/fsl_easrc.h
@@ -601,6 +601,8 @@ struct fsl_easrc_slot {
* @out_missed_sample: sample missed in output
* @st1_addexp: exponent added for stage1
* @st2_addexp: exponent added for stage2
+ * @ratio_mod: update ratio
+ * @in_filled_len: input filled length
*/
struct fsl_easrc_ctx_priv {
struct fsl_easrc_io_params in_params;
@@ -618,6 +620,8 @@ struct fsl_easrc_ctx_priv {
int out_missed_sample;
int st1_addexp;
int st2_addexp;
+ int ratio_mod;
+ unsigned int in_filled_len;
};
/**
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index d0d8a01da9bd..cde0b0c6c1ef 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -119,10 +119,10 @@ static irqreturn_t esai_isr(int irq, void *devid)
dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
if (esr & ESAI_ESR_RFF_MASK)
- dev_warn(&pdev->dev, "isr: Receiving overrun\n");
+ dev_dbg(&pdev->dev, "isr: Receiving overrun\n");
if (esr & ESAI_ESR_TFE_MASK)
- dev_warn(&pdev->dev, "isr: Transmission underrun\n");
+ dev_dbg(&pdev->dev, "isr: Transmission underrun\n");
if (esr & ESAI_ESR_TLS_MASK)
dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
@@ -1189,19 +1189,16 @@ static int fsl_esai_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops fsl_esai_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend,
- fsl_esai_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_esai_runtime_suspend, fsl_esai_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_esai_driver = {
.probe = fsl_esai_probe,
- .remove_new = fsl_esai_remove,
+ .remove = fsl_esai_remove,
.driver = {
.name = "fsl-esai-dai",
- .pm = &fsl_esai_pm_ops,
+ .pm = pm_ptr(&fsl_esai_pm_ops),
.of_match_table = fsl_esai_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c
index 0d37edb70261..aabd90a8b3ec 100644
--- a/sound/soc/fsl/fsl_micfil.c
+++ b/sound/soc/fsl/fsl_micfil.c
@@ -19,6 +19,7 @@
#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/core.h>
@@ -28,6 +29,22 @@
#define MICFIL_OSR_DEFAULT 16
+#define MICFIL_NUM_RATES 7
+#define MICFIL_CLK_SRC_NUM 3
+/* clock source ids */
+#define MICFIL_AUDIO_PLL1 0
+#define MICFIL_AUDIO_PLL2 1
+#define MICFIL_CLK_EXT3 2
+
+static const unsigned int fsl_micfil_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list fsl_micfil_rate_constraints = {
+ .count = ARRAY_SIZE(fsl_micfil_rates),
+ .list = fsl_micfil_rates,
+};
+
enum quality {
QUALITY_HIGH,
QUALITY_MEDIUM,
@@ -45,9 +62,12 @@ struct fsl_micfil {
struct clk *mclk;
struct clk *pll8k_clk;
struct clk *pll11k_clk;
+ struct clk *clk_src[MICFIL_CLK_SRC_NUM];
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct sdma_peripheral_config sdmacfg;
struct snd_soc_card *card;
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ unsigned int constraint_rates_list[MICFIL_NUM_RATES];
unsigned int dataline;
char name[32];
int irq[MICFIL_IRQ_LINES];
@@ -58,6 +78,8 @@ struct fsl_micfil {
int vad_detected;
struct fsl_micfil_verid verid;
struct fsl_micfil_param param;
+ bool mclk_flag; /* mclk enable flag */
+ bool dec_bypass;
};
struct fsl_micfil_soc_data {
@@ -67,7 +89,9 @@ struct fsl_micfil_soc_data {
bool imx;
bool use_edma;
bool use_verid;
+ bool volume_sx;
u64 formats;
+ int fifo_offset;
};
static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
@@ -76,6 +100,8 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mm = {
.fifo_depth = 8,
.dataline = 0xf,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .volume_sx = true,
+ .fifo_offset = 0,
};
static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
@@ -84,6 +110,8 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mp = {
.fifo_depth = 32,
.dataline = 0xf,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .volume_sx = false,
+ .fifo_offset = 0,
};
static struct fsl_micfil_soc_data fsl_micfil_imx93 = {
@@ -94,12 +122,27 @@ static struct fsl_micfil_soc_data fsl_micfil_imx93 = {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.use_edma = true,
.use_verid = true,
+ .volume_sx = false,
+ .fifo_offset = 0,
+};
+
+static struct fsl_micfil_soc_data fsl_micfil_imx943 = {
+ .imx = true,
+ .fifos = 8,
+ .fifo_depth = 32,
+ .dataline = 0xf,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE,
+ .use_edma = true,
+ .use_verid = true,
+ .volume_sx = false,
+ .fifo_offset = -4,
};
static const struct of_device_id fsl_micfil_dt_ids[] = {
{ .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm },
{ .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp },
{ .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 },
+ { .compatible = "fsl,imx943-micfil", .data = &fsl_micfil_imx943 },
{}
};
MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids);
@@ -142,6 +185,8 @@ static int micfil_set_quality(struct fsl_micfil *micfil)
case QUALITY_VLOW2:
qsel = MICFIL_QSEL_VLOW2_QUALITY;
break;
+ default:
+ return -EINVAL;
}
return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
@@ -317,7 +362,26 @@ static int hwvad_detected(struct snd_kcontrol *kcontrol,
return 0;
}
-static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
+static const struct snd_kcontrol_new fsl_micfil_volume_controls[] = {
+ SOC_SINGLE_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(1), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(2), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(3), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(4), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH5 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(5), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH6 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(6), 0xF, 0, gain_tlv),
+ SOC_SINGLE_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL,
+ MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0, gain_tlv),
+};
+
+static const struct snd_kcontrol_new fsl_micfil_volume_sx_controls[] = {
SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL,
MICFIL_OUTGAIN_CHX_SHIFT(0), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH1 Volume", REG_MICFIL_OUT_CTRL,
@@ -334,6 +398,9 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
MICFIL_OUTGAIN_CHX_SHIFT(6), 0x8, 0xF, gain_tlv),
SOC_SINGLE_SX_TLV("CH7 Volume", REG_MICFIL_OUT_CTRL,
MICFIL_OUTGAIN_CHX_SHIFT(7), 0x8, 0xF, gain_tlv),
+};
+
+static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = {
SOC_ENUM_EXT("MICFIL Quality Select",
fsl_micfil_quality_enum,
micfil_quality_get, micfil_quality_set),
@@ -455,6 +522,11 @@ static int fsl_micfil_startup(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ if (micfil->constraint_rates.count > 0)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &micfil->constraint_rates);
+
return 0;
}
@@ -650,23 +722,23 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
/* Enable the module */
ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_PDMIEN);
+ MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN);
if (ret)
return ret;
- if (micfil->vad_enabled)
+ if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_enable(micfil);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (micfil->vad_enabled)
+ if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_disable(micfil);
/* Disable the module */
ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1,
- MICFIL_CTRL1_PDMIEN);
+ MICFIL_CTRL1_PDMIEN | MICFIL_CTRL1_ERREN);
if (ret)
return ret;
@@ -693,7 +765,6 @@ static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int s
clk = micfil->mclk;
/* Disable clock first, for it was enabled by pm_runtime */
- clk_disable_unprepare(clk);
fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk,
micfil->pll11k_clk, ratio);
ret = clk_prepare_enable(clk);
@@ -709,8 +780,9 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
{
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
unsigned int rate = params_rate(params);
- int clk_div = 8;
+ int clk_div = 8, mclk_rate, div_multiply_k;
int osr = MICFIL_OSR_DEFAULT;
int ret;
@@ -730,7 +802,41 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
- ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);
+ micfil->mclk_flag = true;
+
+ /* floor(K * CLKDIV) */
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ div_multiply_k = clk_div >> 1;
+ break;
+ case QUALITY_LOW:
+ case QUALITY_VLOW1:
+ div_multiply_k = clk_div << 1;
+ break;
+ case QUALITY_VLOW2:
+ div_multiply_k = clk_div << 2;
+ break;
+ case QUALITY_MEDIUM:
+ case QUALITY_VLOW0:
+ default:
+ div_multiply_k = clk_div;
+ break;
+ }
+
+ if (format == SNDRV_PCM_FORMAT_DSD_U32_BE) {
+ micfil->dec_bypass = true;
+ /*
+ * According to equation 29 in RM:
+ * MCLK_CLK_ROOT = PDM CLK rate * 2 * floor(K * CLKDIV)
+ * PDM CLK rate = rate * physical bit width (32)
+ */
+ mclk_rate = rate * div_multiply_k * 32 * 2;
+ } else {
+ micfil->dec_bypass = false;
+ mclk_rate = rate * clk_div * osr * 8;
+ }
+
+ ret = clk_set_rate(micfil->mclk, mclk_rate);
if (ret)
return ret;
@@ -738,10 +844,14 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
+ regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_DEC_BYPASS,
+ micfil->dec_bypass ? MICFIL_CTRL2_DEC_BYPASS : 0);
+
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
- FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr));
+ FIELD_PREP(MICFIL_CTRL2_CICOSR, 32 - osr));
/* Configure CIC OSR in VADCICOSR */
regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1,
@@ -764,6 +874,17 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int fsl_micfil_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(micfil->mclk);
+ micfil->mclk_flag = false;
+
+ return 0;
+}
+
static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev);
@@ -801,11 +922,26 @@ static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai)
return 0;
}
+static int fsl_micfil_component_probe(struct snd_soc_component *component)
+{
+ struct fsl_micfil *micfil = snd_soc_component_get_drvdata(component);
+
+ if (micfil->soc->volume_sx)
+ snd_soc_add_component_controls(component, fsl_micfil_volume_sx_controls,
+ ARRAY_SIZE(fsl_micfil_volume_sx_controls));
+ else
+ snd_soc_add_component_controls(component, fsl_micfil_volume_controls,
+ ARRAY_SIZE(fsl_micfil_volume_controls));
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops fsl_micfil_dai_ops = {
.probe = fsl_micfil_dai_probe,
.startup = fsl_micfil_startup,
.trigger = fsl_micfil_trigger,
.hw_params = fsl_micfil_hw_params,
+ .hw_free = fsl_micfil_hw_free,
};
static struct snd_soc_dai_driver fsl_micfil_dai = {
@@ -821,6 +957,7 @@ static struct snd_soc_dai_driver fsl_micfil_dai = {
static const struct snd_soc_component_driver fsl_micfil_component = {
.name = "fsl-micfil-dai",
+ .probe = fsl_micfil_component_probe,
.controls = fsl_micfil_snd_controls,
.num_controls = ARRAY_SIZE(fsl_micfil_snd_controls),
.legacy_dai_naming = 1,
@@ -831,7 +968,7 @@ static const struct reg_default fsl_micfil_reg_defaults[] = {
{REG_MICFIL_CTRL1, 0x00000000},
{REG_MICFIL_CTRL2, 0x00000000},
{REG_MICFIL_STAT, 0x00000000},
- {REG_MICFIL_FIFO_CTRL, 0x00000007},
+ {REG_MICFIL_FIFO_CTRL, 0x0000001F},
{REG_MICFIL_FIFO_STAT, 0x00000000},
{REG_MICFIL_DATACH0, 0x00000000},
{REG_MICFIL_DATACH1, 0x00000000},
@@ -853,28 +990,49 @@ static const struct reg_default fsl_micfil_reg_defaults[] = {
{REG_MICFIL_VAD0_ZCD, 0x00000004},
};
+static const struct reg_default fsl_micfil_reg_defaults_v2[] = {
+ {REG_MICFIL_CTRL1, 0x00000000},
+ {REG_MICFIL_CTRL2, 0x00000000},
+ {REG_MICFIL_STAT, 0x00000000},
+ {REG_MICFIL_FIFO_CTRL, 0x0000001F},
+ {REG_MICFIL_FIFO_STAT, 0x00000000},
+ {REG_MICFIL_DATACH0 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH1 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH2 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH3 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH4 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH5 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH6 - 0x4, 0x00000000},
+ {REG_MICFIL_DATACH7 - 0x4, 0x00000000},
+ {REG_MICFIL_DC_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_CTRL, 0x00000000},
+ {REG_MICFIL_OUT_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL1, 0x00000000},
+ {REG_MICFIL_VAD0_CTRL2, 0x000A0000},
+ {REG_MICFIL_VAD0_STAT, 0x00000000},
+ {REG_MICFIL_VAD0_SCONFIG, 0x00000000},
+ {REG_MICFIL_VAD0_NCONFIG, 0x80000000},
+ {REG_MICFIL_VAD0_NDATA, 0x00000000},
+ {REG_MICFIL_VAD0_ZCD, 0x00000004},
+};
+
static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ofs = micfil->soc->fifo_offset;
+
+ if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs))
+ return true;
+
switch (reg) {
case REG_MICFIL_CTRL1:
case REG_MICFIL_CTRL2:
case REG_MICFIL_STAT:
case REG_MICFIL_FIFO_CTRL:
case REG_MICFIL_FIFO_STAT:
- case REG_MICFIL_DATACH0:
- case REG_MICFIL_DATACH1:
- case REG_MICFIL_DATACH2:
- case REG_MICFIL_DATACH3:
- case REG_MICFIL_DATACH4:
- case REG_MICFIL_DATACH5:
- case REG_MICFIL_DATACH6:
- case REG_MICFIL_DATACH7:
case REG_MICFIL_DC_CTRL:
case REG_MICFIL_OUT_CTRL:
case REG_MICFIL_OUT_STAT:
- case REG_MICFIL_FSYNC_CTRL:
- case REG_MICFIL_VERID:
- case REG_MICFIL_PARAM:
case REG_MICFIL_VAD0_CTRL1:
case REG_MICFIL_VAD0_CTRL2:
case REG_MICFIL_VAD0_STAT:
@@ -883,6 +1041,12 @@ static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg)
case REG_MICFIL_VAD0_NDATA:
case REG_MICFIL_VAD0_ZCD:
return true;
+ case REG_MICFIL_FSYNC_CTRL:
+ case REG_MICFIL_VERID:
+ case REG_MICFIL_PARAM:
+ if (micfil->soc->use_verid)
+ return true;
+ fallthrough;
default:
return false;
}
@@ -890,6 +1054,8 @@ static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+
switch (reg) {
case REG_MICFIL_CTRL1:
case REG_MICFIL_CTRL2:
@@ -899,7 +1065,6 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
case REG_MICFIL_DC_CTRL:
case REG_MICFIL_OUT_CTRL:
case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */
- case REG_MICFIL_FSYNC_CTRL:
case REG_MICFIL_VAD0_CTRL1:
case REG_MICFIL_VAD0_CTRL2:
case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */
@@ -907,6 +1072,10 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
case REG_MICFIL_VAD0_NCONFIG:
case REG_MICFIL_VAD0_ZCD:
return true;
+ case REG_MICFIL_FSYNC_CTRL:
+ if (micfil->soc->use_verid)
+ return true;
+ fallthrough;
default:
return false;
}
@@ -914,16 +1083,16 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg)
static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_micfil *micfil = dev_get_drvdata(dev);
+ int ofs = micfil->soc->fifo_offset;
+
+ if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs))
+ return true;
+
switch (reg) {
case REG_MICFIL_STAT:
- case REG_MICFIL_DATACH0:
- case REG_MICFIL_DATACH1:
- case REG_MICFIL_DATACH2:
- case REG_MICFIL_DATACH3:
- case REG_MICFIL_DATACH4:
- case REG_MICFIL_DATACH5:
- case REG_MICFIL_DATACH6:
- case REG_MICFIL_DATACH7:
+ case REG_MICFIL_FIFO_STAT:
+ case REG_MICFIL_OUT_STAT:
case REG_MICFIL_VERID:
case REG_MICFIL_PARAM:
case REG_MICFIL_VAD0_STAT:
@@ -945,7 +1114,21 @@ static const struct regmap_config fsl_micfil_regmap_config = {
.readable_reg = fsl_micfil_readable_reg,
.volatile_reg = fsl_micfil_volatile_reg,
.writeable_reg = fsl_micfil_writeable_reg,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config fsl_micfil_regmap_config_v2 = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_MICFIL_VAD0_ZCD,
+ .reg_defaults = fsl_micfil_reg_defaults_v2,
+ .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults_v2),
+ .readable_reg = fsl_micfil_readable_reg,
+ .volatile_reg = fsl_micfil_volatile_reg,
+ .writeable_reg = fsl_micfil_writeable_reg,
+ .cache_type = REGCACHE_MAPLE,
};
/* END OF REGMAP */
@@ -978,7 +1161,7 @@ static irqreturn_t micfil_isr(int irq, void *devid)
regmap_write_bits(micfil->regmap,
REG_MICFIL_STAT,
MICFIL_STAT_CHXF(i),
- 1);
+ MICFIL_STAT_CHXF(i));
}
for (i = 0; i < MICFIL_FIFO_NUM; i++) {
@@ -1000,6 +1183,8 @@ static irqreturn_t micfil_err_isr(int irq, void *devid)
{
struct fsl_micfil *micfil = (struct fsl_micfil *)devid;
struct platform_device *pdev = micfil->pdev;
+ u32 fifo_stat_reg;
+ u32 out_stat_reg;
u32 stat_reg;
regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg);
@@ -1013,9 +1198,17 @@ static irqreturn_t micfil_err_isr(int irq, void *devid)
if (stat_reg & MICFIL_STAT_LOWFREQF) {
dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n");
regmap_write_bits(micfil->regmap, REG_MICFIL_STAT,
- MICFIL_STAT_LOWFREQF, 1);
+ MICFIL_STAT_LOWFREQF, MICFIL_STAT_LOWFREQF);
}
+ regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg);
+ regmap_write_bits(micfil->regmap, REG_MICFIL_FIFO_STAT,
+ fifo_stat_reg, fifo_stat_reg);
+
+ regmap_read(micfil->regmap, REG_MICFIL_OUT_STAT, &out_stat_reg);
+ regmap_write_bits(micfil->regmap, REG_MICFIL_OUT_STAT,
+ out_stat_reg, out_stat_reg);
+
return IRQ_HANDLED;
}
@@ -1124,14 +1317,32 @@ static int fsl_micfil_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk,
&micfil->pll11k_clk);
+ micfil->clk_src[MICFIL_AUDIO_PLL1] = micfil->pll8k_clk;
+ micfil->clk_src[MICFIL_AUDIO_PLL2] = micfil->pll11k_clk;
+ micfil->clk_src[MICFIL_CLK_EXT3] = devm_clk_get(&pdev->dev, "clkext3");
+ if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3]))
+ micfil->clk_src[MICFIL_CLK_EXT3] = NULL;
+
+ fsl_asoc_constrain_rates(&micfil->constraint_rates,
+ &fsl_micfil_rate_constraints,
+ micfil->clk_src[MICFIL_AUDIO_PLL1],
+ micfil->clk_src[MICFIL_AUDIO_PLL2],
+ micfil->clk_src[MICFIL_CLK_EXT3],
+ micfil->constraint_rates_list);
+
/* init regmap */
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
- regs,
- &fsl_micfil_regmap_config);
+ if (of_device_is_compatible(np, "fsl,imx943-micfil"))
+ micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
+ regs,
+ &fsl_micfil_regmap_config_v2);
+ else
+ micfil->regmap = devm_regmap_init_mmio(&pdev->dev,
+ regs,
+ &fsl_micfil_regmap_config);
if (IS_ERR(micfil->regmap)) {
dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n",
PTR_ERR(micfil->regmap));
@@ -1200,7 +1411,7 @@ static int fsl_micfil_probe(struct platform_device *pdev)
}
micfil->dma_params_rx.chan_name = "rx";
- micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0;
+ micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0 + micfil->soc->fifo_offset;
micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX;
platform_set_drvdata(pdev, micfil);
@@ -1269,7 +1480,8 @@ static int fsl_micfil_runtime_suspend(struct device *dev)
regcache_cache_only(micfil->regmap, true);
- clk_disable_unprepare(micfil->mclk);
+ if (micfil->mclk_flag)
+ clk_disable_unprepare(micfil->mclk);
clk_disable_unprepare(micfil->busclk);
return 0;
@@ -1284,10 +1496,12 @@ static int fsl_micfil_runtime_resume(struct device *dev)
if (ret < 0)
return ret;
- ret = clk_prepare_enable(micfil->mclk);
- if (ret < 0) {
- clk_disable_unprepare(micfil->busclk);
- return ret;
+ if (micfil->mclk_flag) {
+ ret = clk_prepare_enable(micfil->mclk);
+ if (ret < 0) {
+ clk_disable_unprepare(micfil->busclk);
+ return ret;
+ }
}
regcache_cache_only(micfil->regmap, false);
@@ -1298,19 +1512,16 @@ static int fsl_micfil_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops fsl_micfil_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend,
- fsl_micfil_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, fsl_micfil_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_micfil_driver = {
.probe = fsl_micfil_probe,
- .remove_new = fsl_micfil_remove,
+ .remove = fsl_micfil_remove,
.driver = {
.name = "fsl-micfil-dai",
- .pm = &fsl_micfil_pm_ops,
+ .pm = pm_ptr(&fsl_micfil_pm_ops),
.of_match_table = fsl_micfil_dt_ids,
},
};
diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h
index c6b902ba0a53..fdfe4e7125bc 100644
--- a/sound/soc/fsl/fsl_micfil.h
+++ b/sound/soc/fsl/fsl_micfil.h
@@ -53,6 +53,7 @@
#define MICFIL_CTRL1_CHEN(ch) BIT(ch)
/* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */
+#define MICFIL_CTRL2_DEC_BYPASS BIT(31)
#define MICFIL_CTRL2_QSEL_SHIFT 25
#define MICFIL_CTRL2_QSEL GENMASK(27, 25)
#define MICFIL_QSEL_MEDIUM_QUALITY 0
@@ -62,7 +63,7 @@
#define MICFIL_QSEL_VLOW1_QUALITY 5
#define MICFIL_QSEL_VLOW2_QUALITY 4
-#define MICFIL_CTRL2_CICOSR GENMASK(19, 16)
+#define MICFIL_CTRL2_CICOSR GENMASK(20, 16)
#define MICFIL_CTRL2_CLKDIV GENMASK(7, 0)
/* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */
@@ -72,7 +73,7 @@
#define MICFIL_STAT_CHXF(ch) BIT(ch)
/* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */
-#define MICFIL_FIFO_CTRL_FIFOWMK GENMASK(2, 0)
+#define MICFIL_FIFO_CTRL_FIFOWMK GENMASK(4, 0)
/* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */
#define MICFIL_FIFO_STAT_FIFOX_OVER(ch) BIT(ch)
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index 60929c36a0e3..e34e5ea98de5 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -6,6 +6,7 @@
// Copyright 2019 NXP
#include <linux/clk.h>
+#include <linux/firmware/imx/sm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mfd/syscon.h>
@@ -28,10 +29,16 @@
#define MQS_CLK_DIV_MASK (0xFF << 0)
#define MQS_CLK_DIV_SHIFT (0)
+enum reg_type {
+ TYPE_REG_OWN, /* module own register space */
+ TYPE_REG_GPR, /* register in GPR space */
+ TYPE_REG_SM, /* System Manager controls the register */
+};
+
/**
* struct fsl_mqs_soc_data - soc specific data
*
- * @use_gpr: control register is in General Purpose Register group
+ * @type: control register space type
* @ctrl_off: control register offset
* @en_mask: enable bit mask
* @en_shift: enable bit shift
@@ -43,7 +50,7 @@
* @div_shift: clock divider bit shift
*/
struct fsl_mqs_soc_data {
- bool use_gpr;
+ enum reg_type type;
int ctrl_off;
int en_mask;
int en_shift;
@@ -68,6 +75,29 @@ struct fsl_mqs {
#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_mqs *mqs_priv = context;
+ int num = 1;
+
+ if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
+ mqs_priv->soc->ctrl_off == reg)
+ return scmi_imx_misc_ctrl_get(SCMI_IMX_CTRL_MQS1_SETTINGS, &num, val);
+
+ return -EINVAL;
+};
+
+static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_mqs *mqs_priv = context;
+
+ if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
+ mqs_priv->soc->ctrl_off == reg)
+ return scmi_imx_misc_ctrl_set(SCMI_IMX_CTRL_MQS1_SETTINGS, val);
+
+ return -EINVAL;
+};
+
static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -182,6 +212,13 @@ static const struct regmap_config fsl_mqs_regmap_config = {
.cache_type = REGCACHE_NONE,
};
+static const struct regmap_config fsl_mqs_sm_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_read = fsl_mqs_sm_read,
+ .reg_write = fsl_mqs_sm_write,
+};
+
static int fsl_mqs_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -200,7 +237,7 @@ static int fsl_mqs_probe(struct platform_device *pdev)
*/
mqs_priv->soc = of_device_get_match_data(&pdev->dev);
- if (mqs_priv->soc->use_gpr) {
+ if (mqs_priv->soc->type == TYPE_REG_GPR) {
gpr_np = of_parse_phandle(np, "gpr", 0);
if (!gpr_np) {
dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
@@ -213,6 +250,16 @@ static int fsl_mqs_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get gpr regmap\n");
return PTR_ERR(mqs_priv->regmap);
}
+ } else if (mqs_priv->soc->type == TYPE_REG_SM) {
+ mqs_priv->regmap = devm_regmap_init(&pdev->dev,
+ NULL,
+ mqs_priv,
+ &fsl_mqs_sm_regmap);
+ if (IS_ERR(mqs_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
+ PTR_ERR(mqs_priv->regmap));
+ return PTR_ERR(mqs_priv->regmap);
+ }
} else {
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
@@ -259,7 +306,6 @@ static void fsl_mqs_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#ifdef CONFIG_PM
static int fsl_mqs_runtime_resume(struct device *dev)
{
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
@@ -293,18 +339,14 @@ static int fsl_mqs_runtime_suspend(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops fsl_mqs_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend,
- fsl_mqs_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_mqs_runtime_suspend, fsl_mqs_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = {
- .use_gpr = false,
+ .type = TYPE_REG_OWN,
.ctrl_off = REG_MQS_CTRL,
.en_mask = MQS_EN_MASK,
.en_shift = MQS_EN_SHIFT,
@@ -317,7 +359,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = {
};
static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = {
- .use_gpr = true,
+ .type = TYPE_REG_GPR,
.ctrl_off = IOMUXC_GPR2,
.en_mask = IMX6SX_GPR2_MQS_EN_MASK,
.en_shift = IMX6SX_GPR2_MQS_EN_SHIFT,
@@ -330,7 +372,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = {
};
static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = {
- .use_gpr = true,
+ .type = TYPE_REG_GPR,
.ctrl_off = 0x20,
.en_mask = BIT(1),
.en_shift = 1,
@@ -342,21 +384,77 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = {
.div_shift = 8,
};
+static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = {
+ .type = TYPE_REG_SM,
+ .ctrl_off = 0x88,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = {
+ .type = TYPE_REG_GPR,
+ .ctrl_off = 0x0,
+ .en_mask = BIT(2),
+ .en_shift = 2,
+ .rst_mask = BIT(3),
+ .rst_shift = 3,
+ .osr_mask = BIT(4),
+ .osr_shift = 4,
+ .div_mask = GENMASK(16, 9),
+ .div_shift = 9,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = {
+ .type = TYPE_REG_SM,
+ .ctrl_off = 0x88,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
+static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = {
+ .type = TYPE_REG_GPR,
+ .ctrl_off = 0x10,
+ .en_mask = BIT(1),
+ .en_shift = 1,
+ .rst_mask = BIT(2),
+ .rst_shift = 2,
+ .osr_mask = BIT(3),
+ .osr_shift = 3,
+ .div_mask = GENMASK(15, 8),
+ .div_shift = 8,
+};
+
static const struct of_device_id fsl_mqs_dt_ids[] = {
{ .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data },
{ .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data },
{ .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data },
+ { .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data },
+ { .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data },
+ { .compatible = "fsl,imx943-aonmix-mqs", .data = &fsl_mqs_imx943_aon_data },
+ { .compatible = "fsl,imx943-wakeupmix-mqs", .data = &fsl_mqs_imx943_wakeup_data },
{}
};
MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
static struct platform_driver fsl_mqs_driver = {
.probe = fsl_mqs_probe,
- .remove_new = fsl_mqs_remove,
+ .remove = fsl_mqs_remove,
.driver = {
.name = "fsl-mqs",
.of_match_table = fsl_mqs_dt_ids,
- .pm = &fsl_mqs_pm_ops,
+ .pm = pm_ptr(&fsl_mqs_pm_ops),
},
};
diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c
index bfaaa451735b..5614a8b909ed 100644
--- a/sound/soc/fsl/fsl_qmc_audio.c
+++ b/sound/soc/fsl/fsl_qmc_audio.c
@@ -17,13 +17,23 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
+struct qmc_dai_chan {
+ struct qmc_dai_prtd *prtd_tx;
+ struct qmc_dai_prtd *prtd_rx;
+ struct qmc_chan *qmc_chan;
+};
+
struct qmc_dai {
char *name;
int id;
struct device *dev;
- struct qmc_chan *qmc_chan;
unsigned int nb_tx_ts;
unsigned int nb_rx_ts;
+
+ unsigned int nb_chans_avail;
+ unsigned int nb_chans_used_tx;
+ unsigned int nb_chans_used_rx;
+ struct qmc_dai_chan *chans;
};
struct qmc_audio {
@@ -35,11 +45,19 @@ struct qmc_audio {
struct qmc_dai_prtd {
struct qmc_dai *qmc_dai;
- dma_addr_t dma_buffer_start;
- dma_addr_t period_ptr_submitted;
- dma_addr_t period_ptr_ended;
- dma_addr_t dma_buffer_end;
- size_t period_size;
+
+ snd_pcm_uframes_t buffer_ended;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+
+ dma_addr_t ch_dma_addr_start;
+ dma_addr_t ch_dma_addr_current;
+ dma_addr_t ch_dma_addr_end;
+ size_t ch_dma_size;
+ size_t ch_dma_offset;
+
+ unsigned int channels;
+ DECLARE_BITMAP(chans_pending, 64);
struct snd_pcm_substream *substream;
};
@@ -54,10 +72,22 @@ static int qmc_audio_pcm_construct(struct snd_soc_component *component,
return ret;
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, card->dev,
- 64*1024, 64*1024);
+ 64 * 1024, 64 * 1024);
return 0;
}
+static bool qmc_audio_access_is_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -65,66 +95,143 @@ static int qmc_audio_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
- prtd->dma_buffer_start = runtime->dma_addr;
- prtd->dma_buffer_end = runtime->dma_addr + params_buffer_bytes(params);
- prtd->period_size = params_period_bytes(params);
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
- prtd->period_ptr_ended = prtd->dma_buffer_start;
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ prtd->channels = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
prtd->substream = substream;
+ prtd->buffer_ended = 0;
+ prtd->buffer_size = params_buffer_size(params);
+ prtd->period_size = params_period_size(params);
+
+ prtd->ch_dma_addr_start = runtime->dma_addr;
+ prtd->ch_dma_offset = params_buffer_bytes(params) / prtd->channels;
+ prtd->ch_dma_addr_end = runtime->dma_addr + prtd->ch_dma_offset;
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+ prtd->ch_dma_size = params_period_bytes(params) / prtd->channels;
+
return 0;
}
-static void qmc_audio_pcm_write_complete(void *context)
+static void qmc_audio_pcm_write_complete(void *context);
+
+static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd)
{
- struct qmc_dai_prtd *prtd = context;
+ unsigned int i;
int ret;
- prtd->period_ptr_ended += prtd->period_size;
- if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
- prtd->period_ptr_ended = prtd->dma_buffer_start;
-
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ for (i = 0; i < prtd->channels; i++) {
+ bitmap_set(prtd->chans_pending, i, 1);
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
- if (ret) {
- dev_err(prtd->qmc_dai->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan,
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ qmc_audio_pcm_write_complete,
+ &prtd->qmc_dai->chans[i]);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n",
+ i, ret);
+ bitmap_clear(prtd->chans_pending, i, 1);
+ return ret;
+ }
}
+ return 0;
+}
+
+static void qmc_audio_pcm_write_complete(void *context)
+{
+ struct qmc_dai_chan *chan = context;
+ struct qmc_dai_prtd *prtd;
+
+ prtd = chan->prtd_tx;
+
+ /* Mark the current channel as completed */
+ bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
+ /*
+ * All QMC channels involved must have completed their transfer before
+ * submitting a new one.
+ */
+ if (!bitmap_empty(prtd->chans_pending, 64))
+ return;
+
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
+
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ qmc_audio_pcm_write_submit(prtd);
+
snd_pcm_period_elapsed(prtd->substream);
}
-static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags);
+
+static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd)
{
- struct qmc_dai_prtd *prtd = context;
+ unsigned int i;
int ret;
- if (length != prtd->period_size) {
- dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
- length, prtd->period_size);
+ for (i = 0; i < prtd->channels; i++) {
+ bitmap_set(prtd->chans_pending, i, 1);
+
+ ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan,
+ prtd->ch_dma_addr_current + i * prtd->ch_dma_offset,
+ prtd->ch_dma_size,
+ qmc_audio_pcm_read_complete,
+ &prtd->qmc_dai->chans[i]);
+ if (ret) {
+ dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n",
+ i, ret);
+ bitmap_clear(prtd->chans_pending, i, 1);
+ return ret;
+ }
}
- prtd->period_ptr_ended += prtd->period_size;
- if (prtd->period_ptr_ended >= prtd->dma_buffer_end)
- prtd->period_ptr_ended = prtd->dma_buffer_start;
+ return 0;
+}
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags)
+{
+ struct qmc_dai_chan *chan = context;
+ struct qmc_dai_prtd *prtd;
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
- if (ret) {
- dev_err(prtd->qmc_dai->dev, "read_submit failed %d\n",
- ret);
+ prtd = chan->prtd_rx;
+
+ /* Mark the current channel as completed */
+ bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1);
+
+ if (length != prtd->ch_dma_size) {
+ dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n",
+ length, prtd->ch_dma_size);
}
+ /*
+ * All QMC channels involved must have completed their transfer before
+ * submitting a new one.
+ */
+ if (!bitmap_empty(prtd->chans_pending, 64))
+ return;
+
+ prtd->buffer_ended += prtd->period_size;
+ if (prtd->buffer_ended >= prtd->buffer_size)
+ prtd->buffer_ended = 0;
+
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
+ qmc_audio_pcm_read_submit(prtd);
+
snd_pcm_period_elapsed(prtd->substream);
}
@@ -132,6 +239,7 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ unsigned int i;
int ret;
if (!prtd->qmc_dai) {
@@ -141,56 +249,46 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ bitmap_zero(prtd->chans_pending, 64);
+ prtd->buffer_ended = 0;
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < prtd->channels; i++)
+ prtd->qmc_dai->chans[i].prtd_tx = prtd;
+
/* Submit first chunk ... */
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
return ret;
- }
/* ... prepare next one ... */
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_write_complete, prtd);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_write_submit(prtd);
+ if (ret)
return ret;
- }
} else {
+ for (i = 0; i < prtd->channels; i++)
+ prtd->qmc_dai->chans[i].prtd_rx = prtd;
+
/* Submit first chunk ... */
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
- if (ret) {
- dev_err(component->dev, "read_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
return ret;
- }
/* ... prepare next one ... */
- prtd->period_ptr_submitted += prtd->period_size;
- if (prtd->period_ptr_submitted >= prtd->dma_buffer_end)
- prtd->period_ptr_submitted = prtd->dma_buffer_start;
+ prtd->ch_dma_addr_current += prtd->ch_dma_size;
+ if (prtd->ch_dma_addr_current >= prtd->ch_dma_addr_end)
+ prtd->ch_dma_addr_current = prtd->ch_dma_addr_start;
/* ... and send it */
- ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chan,
- prtd->period_ptr_submitted, prtd->period_size,
- qmc_audio_pcm_read_complete, prtd);
- if (ret) {
- dev_err(component->dev, "write_submit failed %d\n",
- ret);
+ ret = qmc_audio_pcm_read_submit(prtd);
+ if (ret)
return ret;
- }
}
break;
@@ -215,13 +313,12 @@ static snd_pcm_uframes_t qmc_audio_pcm_pointer(struct snd_soc_component *compone
{
struct qmc_dai_prtd *prtd = substream->runtime->private_data;
- return bytes_to_frames(substream->runtime,
- prtd->period_ptr_ended - prtd->dma_buffer_start);
+ return prtd->buffer_ended;
}
static int qmc_audio_of_xlate_dai_name(struct snd_soc_component *component,
- const struct of_phandle_args *args,
- const char **dai_name)
+ const struct of_phandle_args *args,
+ const char **dai_name)
{
struct qmc_audio *qmc_audio = dev_get_drvdata(component->dev);
struct snd_soc_dai_driver *dai_driver;
@@ -243,12 +340,13 @@ static const struct snd_pcm_hardware qmc_audio_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_PAUSE,
.period_bytes_min = 32,
- .period_bytes_max = 64*1024,
+ .period_bytes_max = 64 * 1024,
.periods_min = 2,
- .periods_max = 2*1024,
- .buffer_bytes_max = 64*1024,
+ .periods_max = 2 * 1024,
+ .buffer_bytes_max = 64 * 1024,
};
static int qmc_audio_pcm_open(struct snd_soc_component *component,
@@ -266,7 +364,7 @@ static int qmc_audio_pcm_open(struct snd_soc_component *component,
return ret;
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL)
+ if (!prtd)
return -ENOMEM;
runtime->private_data = prtd;
@@ -329,13 +427,13 @@ static int qmc_dai_hw_rule_channels_by_format(struct qmc_dai *qmc_dai,
ch.max = nb_ts;
break;
case 16:
- ch.max = nb_ts/2;
+ ch.max = nb_ts / 2;
break;
case 32:
- ch.max = nb_ts/4;
+ ch.max = nb_ts / 4;
break;
case 64:
- ch.max = nb_ts/8;
+ ch.max = nb_ts / 8;
break;
default:
dev_err(qmc_dai->dev, "format physical width %u not supported\n",
@@ -356,9 +454,8 @@ static int qmc_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params
return qmc_dai_hw_rule_channels_by_format(qmc_dai, params, qmc_dai->nb_tx_ts);
}
-static int qmc_dai_hw_rule_capture_channels_by_format(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
@@ -394,42 +491,31 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai,
return snd_mask_refine(f_old, &f_new);
}
-static int qmc_dai_hw_rule_playback_format_by_channels(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_tx_ts);
}
-static int qmc_dai_hw_rule_capture_format_by_channels(
- struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
+static int qmc_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
{
struct qmc_dai *qmc_dai = rule->private;
return qmc_dai_hw_rule_format_by_channels(qmc_dai, params, qmc_dai->nb_rx_ts);
}
-static int qmc_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
{
- struct qmc_dai_prtd *prtd = substream->runtime->private_data;
snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
- struct qmc_dai *qmc_dai;
unsigned int frame_bits;
+ u64 access;
int ret;
- qmc_dai = qmc_dai_get_data(dai);
- if (!qmc_dai) {
- dev_err(dai->dev, "Invalid dai\n");
- return -EINVAL;
- }
-
- prtd->qmc_dai = qmc_dai;
-
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw_rule_channels_by_format = qmc_dai_hw_rule_capture_channels_by_format;
hw_rule_format_by_channels = qmc_dai_hw_rule_capture_format_by_channels;
@@ -444,7 +530,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
hw_rule_channels_by_format, qmc_dai,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret) {
- dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add channels rule (%d)\n", ret);
return ret;
}
@@ -452,7 +538,7 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
hw_rule_format_by_channels, qmc_dai,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (ret) {
- dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add format rule (%d)\n", ret);
return ret;
}
@@ -460,19 +546,78 @@ static int qmc_dai_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_FRAME_BITS,
frame_bits);
if (ret < 0) {
- dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
return ret;
}
return 0;
}
+static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substream,
+ struct qmc_dai *qmc_dai)
+{
+ unsigned int frame_bits;
+ u64 access;
+ int ret;
+
+ frame_bits = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
+ qmc_dai->nb_rx_ts * 8 : qmc_dai->nb_tx_ts * 8;
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(qmc_dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED |
+ 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS,
+ access);
+ if (ret) {
+ dev_err(qmc_dai->dev, "Failed to add hw_param_access constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qmc_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct qmc_dai_prtd *prtd = substream->runtime->private_data;
+ struct qmc_dai *qmc_dai;
+
+ qmc_dai = qmc_dai_get_data(dai);
+ if (!qmc_dai) {
+ dev_err(dai->dev, "Invalid dai\n");
+ return -EINVAL;
+ }
+
+ prtd->qmc_dai = qmc_dai;
+
+ return qmc_dai->nb_chans_avail > 1 ?
+ qmc_dai_constraints_noninterleaved(substream, qmc_dai) :
+ qmc_dai_constraints_interleaved(substream, qmc_dai);
+}
+
static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct qmc_chan_param chan_param = {0};
+ unsigned int nb_chans_used;
struct qmc_dai *qmc_dai;
+ unsigned int i;
int ret;
qmc_dai = qmc_dai_get_data(dai);
@@ -481,15 +626,34 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /*
+ * In interleaved mode, the driver uses one QMC channel for all audio
+ * channels whereas in non-interleaved mode, it uses one QMC channel per
+ * audio channel.
+ */
+ nb_chans_used = qmc_audio_access_is_interleaved(params_access(params)) ?
+ 1 : params_channels(params);
+
+ if (nb_chans_used > qmc_dai->nb_chans_avail) {
+ dev_err(dai->dev, "Not enough qmc_chans. Need %u, avail %u\n",
+ nb_chans_used, qmc_dai->nb_chans_avail);
+ return -EINVAL;
+ }
+
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
chan_param.mode = QMC_TRANSPARENT;
- chan_param.transp.max_rx_buf_size = params_period_bytes(params);
- ret = qmc_chan_set_param(qmc_dai->qmc_chan, &chan_param);
- if (ret) {
- dev_err(dai->dev, "set param failed %d\n",
- ret);
- return ret;
+ chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used;
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param);
+ if (ret) {
+ dev_err(dai->dev, "chans[%u], set param failed %d\n",
+ i, ret);
+ return ret;
+ }
}
+ qmc_dai->nb_chans_used_rx = nb_chans_used;
+ } else {
+ qmc_dai->nb_chans_used_tx = nb_chans_used;
}
return 0;
@@ -498,9 +662,12 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream,
static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
+ unsigned int nb_chans_used;
struct qmc_dai *qmc_dai;
+ unsigned int i;
int direction;
- int ret;
+ int ret = 0;
+ int ret_tmp;
qmc_dai = qmc_dai_get_data(dai);
if (!qmc_dai) {
@@ -508,30 +675,50 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
}
- direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- QMC_CHAN_WRITE : QMC_CHAN_READ;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = QMC_CHAN_WRITE;
+ nb_chans_used = qmc_dai->nb_chans_used_tx;
+ } else {
+ direction = QMC_CHAN_READ;
+ nb_chans_used = qmc_dai->nb_chans_used_rx;
+ }
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = qmc_chan_start(qmc_dai->qmc_chan, direction);
- if (ret)
- return ret;
+ for (i = 0; i < nb_chans_used; i++) {
+ ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction);
+ if (ret)
+ goto err_stop;
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
- ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
- if (ret)
- return ret;
- ret = qmc_chan_reset(qmc_dai->qmc_chan, direction);
+ /* Stop and reset all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ if (ret_tmp)
+ continue;
+
+ ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
if (ret)
return ret;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = qmc_chan_stop(qmc_dai->qmc_chan, direction);
+ /* Stop all QMC channels and return the first error encountered */
+ for (i = 0; i < nb_chans_used; i++) {
+ ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ if (!ret)
+ ret = ret_tmp;
+ }
if (ret)
return ret;
break;
@@ -541,6 +728,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
}
return 0;
+
+err_stop:
+ while (i--) {
+ qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction);
+ qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction);
+ }
+ return ret;
}
static const struct snd_soc_dai_ops qmc_dai_ops = {
@@ -549,7 +743,7 @@ static const struct snd_soc_dai_ops qmc_dai_ops = {
.hw_params = qmc_dai_hw_params,
};
-static u64 qmc_audio_formats(u8 nb_ts)
+static u64 qmc_audio_formats(u8 nb_ts, bool is_noninterleaved)
{
unsigned int format_width;
unsigned int chan_width;
@@ -581,15 +775,29 @@ static u64 qmc_audio_formats(u8 nb_ts)
if (format_width > chan_width || chan_width % format_width)
continue;
+ /*
+ * In non interleaved mode, we can only support formats that
+ * can fit only 1 time in the channel
+ */
+ if (is_noninterleaved && format_width != chan_width)
+ continue;
+
formats_mask |= pcm_format_to_bits(format);
}
return formats_mask;
}
static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np,
- struct qmc_dai *qmc_dai, struct snd_soc_dai_driver *qmc_soc_dai_driver)
+ struct qmc_dai *qmc_dai,
+ struct snd_soc_dai_driver *qmc_soc_dai_driver)
{
struct qmc_chan_info info;
+ unsigned long rx_fs_rate;
+ unsigned long tx_fs_rate;
+ unsigned int nb_tx_ts;
+ unsigned int nb_rx_ts;
+ unsigned int i;
+ int count;
u32 val;
int ret;
@@ -604,57 +812,106 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *
qmc_dai->name = devm_kasprintf(qmc_audio->dev, GFP_KERNEL, "%s.%d",
np->parent->name, qmc_dai->id);
+ if (!qmc_dai->name)
+ return -ENOMEM;
- qmc_dai->qmc_chan = devm_qmc_chan_get_byphandle(qmc_audio->dev, np,
- "fsl,qmc-chan");
- if (IS_ERR(qmc_dai->qmc_chan)) {
- ret = PTR_ERR(qmc_dai->qmc_chan);
- return dev_err_probe(qmc_audio->dev, ret,
- "dai %d get QMC channel failed\n", qmc_dai->id);
- }
+ count = qmc_chan_count_phandles(np, "fsl,qmc-chan");
+ if (count < 0)
+ return dev_err_probe(qmc_audio->dev, count,
+ "dai %d get number of QMC channel failed\n", qmc_dai->id);
+ if (!count)
+ return dev_err_probe(qmc_audio->dev, -EINVAL,
+ "dai %d no QMC channel defined\n", qmc_dai->id);
- qmc_soc_dai_driver->id = qmc_dai->id;
- qmc_soc_dai_driver->name = qmc_dai->name;
+ qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL);
+ if (!qmc_dai->chans)
+ return -ENOMEM;
- ret = qmc_chan_get_info(qmc_dai->qmc_chan, &info);
- if (ret) {
- dev_err(qmc_audio->dev, "dai %d get QMC channel info failed %d\n",
- qmc_dai->id, ret);
- return ret;
- }
- dev_info(qmc_audio->dev, "dai %d QMC channel mode %d, nb_tx_ts %u, nb_rx_ts %u\n",
- qmc_dai->id, info.mode, info.nb_tx_ts, info.nb_rx_ts);
+ for (i = 0; i < count; i++) {
+ qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np,
+ "fsl,qmc-chan", i);
+ if (IS_ERR(qmc_dai->chans[i].qmc_chan)) {
+ return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan),
+ "dai %d get QMC channel %d failed\n", qmc_dai->id, i);
+ }
- if (info.mode != QMC_TRANSPARENT) {
- dev_err(qmc_audio->dev, "dai %d QMC chan mode %d is not QMC_TRANSPARENT\n",
- qmc_dai->id, info.mode);
- return -EINVAL;
+ ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info);
+ if (ret) {
+ dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n",
+ qmc_dai->id, i, ret);
+ return ret;
+ }
+
+ if (info.mode != QMC_TRANSPARENT) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d mode %d is not QMC_TRANSPARENT\n",
+ qmc_dai->id, i, info.mode);
+ return -EINVAL;
+ }
+
+ /*
+ * All channels must have the same number of Tx slots and the
+ * same numbers of Rx slots.
+ */
+ if (i == 0) {
+ nb_tx_ts = info.nb_tx_ts;
+ nb_rx_ts = info.nb_rx_ts;
+ tx_fs_rate = info.tx_fs_rate;
+ rx_fs_rate = info.rx_fs_rate;
+ } else {
+ if (nb_tx_ts != info.nb_tx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Tx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_tx_ts, nb_tx_ts);
+ return -EINVAL;
+ }
+ if (nb_rx_ts != info.nb_rx_ts) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent number of Rx timeslots (%u instead of %u)\n",
+ qmc_dai->id, i, info.nb_rx_ts, nb_rx_ts);
+ return -EINVAL;
+ }
+ if (tx_fs_rate != info.tx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Tx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.tx_fs_rate, tx_fs_rate);
+ return -EINVAL;
+ }
+ if (rx_fs_rate != info.rx_fs_rate) {
+ dev_err(qmc_audio->dev, "dai %d QMC chan %d inconsistent Rx frame sample rate (%lu instead of %lu)\n",
+ qmc_dai->id, i, info.rx_fs_rate, rx_fs_rate);
+ return -EINVAL;
+ }
+ }
}
- qmc_dai->nb_tx_ts = info.nb_tx_ts;
- qmc_dai->nb_rx_ts = info.nb_rx_ts;
+
+ qmc_dai->nb_chans_avail = count;
+ qmc_dai->nb_tx_ts = nb_tx_ts * count;
+ qmc_dai->nb_rx_ts = nb_rx_ts * count;
+
+ qmc_soc_dai_driver->id = qmc_dai->id;
+ qmc_soc_dai_driver->name = qmc_dai->name;
qmc_soc_dai_driver->playback.channels_min = 0;
qmc_soc_dai_driver->playback.channels_max = 0;
- if (qmc_dai->nb_tx_ts) {
+ if (nb_tx_ts) {
qmc_soc_dai_driver->playback.channels_min = 1;
- qmc_soc_dai_driver->playback.channels_max = qmc_dai->nb_tx_ts;
+ qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts;
}
- qmc_soc_dai_driver->playback.formats = qmc_audio_formats(qmc_dai->nb_tx_ts);
+ qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts,
+ count > 1);
qmc_soc_dai_driver->capture.channels_min = 0;
qmc_soc_dai_driver->capture.channels_max = 0;
- if (qmc_dai->nb_rx_ts) {
+ if (nb_rx_ts) {
qmc_soc_dai_driver->capture.channels_min = 1;
- qmc_soc_dai_driver->capture.channels_max = qmc_dai->nb_rx_ts;
+ qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts;
}
- qmc_soc_dai_driver->capture.formats = qmc_audio_formats(qmc_dai->nb_rx_ts);
+ qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts,
+ count > 1);
- qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(info.tx_fs_rate);
- qmc_soc_dai_driver->playback.rate_min = info.tx_fs_rate;
- qmc_soc_dai_driver->playback.rate_max = info.tx_fs_rate;
- qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(info.rx_fs_rate);
- qmc_soc_dai_driver->capture.rate_min = info.rx_fs_rate;
- qmc_soc_dai_driver->capture.rate_max = info.rx_fs_rate;
+ qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate);
+ qmc_soc_dai_driver->playback.rate_min = tx_fs_rate;
+ qmc_soc_dai_driver->playback.rate_max = tx_fs_rate;
+ qmc_soc_dai_driver->capture.rates = snd_pcm_rate_to_rate_bit(rx_fs_rate);
+ qmc_soc_dai_driver->capture.rate_min = rx_fs_rate;
+ qmc_soc_dai_driver->capture.rate_max = rx_fs_rate;
qmc_soc_dai_driver->ops = &qmc_dai_ops;
@@ -702,7 +959,6 @@ static int qmc_audio_probe(struct platform_device *pdev)
i++;
}
-
platform_set_drvdata(pdev, qmc_audio);
ret = devm_snd_soc_register_component(qmc_audio->dev,
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
index 00852f174a69..5708b3a9878d 100644
--- a/sound/soc/fsl/fsl_rpmsg.c
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -24,6 +24,8 @@
/* 192kHz/32bit/2ch/60s size is 0x574e00 */
#define LPA_LARGE_BUFFER_SIZE (0x6000000)
+/* 16kHz/32bit/8ch/1s size is 0x7D000 */
+#define LPA_CAPTURE_BUFFER_SIZE (0x100000)
static const unsigned int fsl_rpmsg_rates[] = {
8000, 11025, 16000, 22050, 44100,
@@ -97,13 +99,9 @@ static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- int ret;
-
- ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &fsl_rpmsg_rate_constraints);
-
- return ret;
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &fsl_rpmsg_rate_constraints);
}
static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
@@ -135,7 +133,6 @@ static struct snd_soc_dai_driver fsl_rpmsg_dai = {
static const struct snd_soc_component_driver fsl_component = {
.name = "fsl-rpmsg",
- .legacy_dai_naming = 1,
};
static const struct fsl_rpmsg_soc_data imx7ulp_data = {
@@ -176,6 +173,14 @@ static const struct fsl_rpmsg_soc_data imx93_data = {
SNDRV_PCM_FMTBIT_S32_LE,
};
+static const struct fsl_rpmsg_soc_data imx95_data = {
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+};
+
static const struct of_device_id fsl_rpmsg_ids[] = {
{ .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data},
{ .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data},
@@ -183,6 +188,7 @@ static const struct of_device_id fsl_rpmsg_ids[] = {
{ .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data},
{ .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data},
{ .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data},
+ { .compatible = "fsl,imx95-rpmsg-audio", .data = &imx95_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
@@ -190,25 +196,58 @@ MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
static int fsl_rpmsg_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_dai_driver *dai_drv;
+ const char *dai_name;
struct fsl_rpmsg *rpmsg;
int ret;
+ dai_drv = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+ memcpy(dai_drv, &fsl_rpmsg_dai, sizeof(fsl_rpmsg_dai));
+
rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
if (!rpmsg)
return -ENOMEM;
rpmsg->soc_data = of_device_get_match_data(&pdev->dev);
- fsl_rpmsg_dai.playback.rates = rpmsg->soc_data->rates;
- fsl_rpmsg_dai.capture.rates = rpmsg->soc_data->rates;
- fsl_rpmsg_dai.playback.formats = rpmsg->soc_data->formats;
- fsl_rpmsg_dai.capture.formats = rpmsg->soc_data->formats;
+ if (rpmsg->soc_data) {
+ dai_drv->playback.rates = rpmsg->soc_data->rates;
+ dai_drv->capture.rates = rpmsg->soc_data->rates;
+ dai_drv->playback.formats = rpmsg->soc_data->formats;
+ dai_drv->capture.formats = rpmsg->soc_data->formats;
+ }
+
+ /* Use rpmsg channel name as cpu dai name */
+ ret = of_property_read_string(np, "fsl,rpmsg-channel-name", &dai_name);
+ if (ret) {
+ if (ret == -EINVAL) {
+ dai_name = "rpmsg-audio-channel";
+ } else {
+ dev_err(&pdev->dev, "Failed to get rpmsg channel name: %d!\n", ret);
+ return ret;
+ }
+ }
+ dai_drv->name = dai_name;
+
+ /* Setup cpu dai for sound card that sits on rpmsg-micfil-channel */
+ if (!strcmp(dai_name, "rpmsg-micfil-channel")) {
+ dai_drv->capture.channels_min = 1;
+ dai_drv->capture.channels_max = 8;
+ dai_drv->capture.rates = SNDRV_PCM_RATE_8000_48000;
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ if (of_device_is_compatible(np, "fsl,imx8mm-rpmsg-audio"))
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ }
if (of_property_read_bool(np, "fsl,enable-lpa")) {
rpmsg->enable_lpa = 1;
- rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = LPA_LARGE_BUFFER_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = LPA_CAPTURE_BUFFER_SIZE;
} else {
- rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = IMX_DEFAULT_DMABUF_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = IMX_DEFAULT_DMABUF_SIZE;
}
/* Get the optional clocks */
@@ -236,21 +275,10 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
- &fsl_rpmsg_dai, 1);
+ dai_drv, 1);
if (ret)
goto err_pm_disable;
- rpmsg->card_pdev = platform_device_register_data(&pdev->dev,
- "imx-audio-rpmsg",
- PLATFORM_DEVID_AUTO,
- NULL,
- 0);
- if (IS_ERR(rpmsg->card_pdev)) {
- dev_err(&pdev->dev, "failed to register rpmsg card\n");
- ret = PTR_ERR(rpmsg->card_pdev);
- goto err_pm_disable;
- }
-
return 0;
err_pm_disable:
@@ -268,7 +296,6 @@ static void fsl_rpmsg_remove(struct platform_device *pdev)
platform_device_unregister(rpmsg->card_pdev);
}
-#ifdef CONFIG_PM
static int fsl_rpmsg_runtime_resume(struct device *dev)
{
struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
@@ -303,20 +330,18 @@ static int fsl_rpmsg_runtime_suspend(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend,
- fsl_rpmsg_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend, fsl_rpmsg_runtime_resume,
+ NULL)
};
static struct platform_driver fsl_rpmsg_driver = {
.probe = fsl_rpmsg_probe,
- .remove_new = fsl_rpmsg_remove,
+ .remove = fsl_rpmsg_remove,
.driver = {
.name = "fsl_rpmsg",
- .pm = &fsl_rpmsg_pm_ops,
+ .pm = pm_ptr(&fsl_rpmsg_pm_ops),
.of_match_table = fsl_rpmsg_ids,
},
};
diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h
index b04086fbf828..1b1683808507 100644
--- a/sound/soc/fsl/fsl_rpmsg.h
+++ b/sound/soc/fsl/fsl_rpmsg.h
@@ -42,6 +42,6 @@ struct fsl_rpmsg {
unsigned int mclk_streams;
int force_lpa;
int enable_lpa;
- int buffer_size;
+ int buffer_size[2];
};
#endif /* __FSL_RPMSG_H */
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 0e2c31439670..af1a168d35e3 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -163,14 +163,46 @@ out:
return iret;
}
-static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
- u32 rx_mask, int slots, int slot_width)
+static int fsl_sai_set_dai_tdm_slot_tx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = true;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot_rx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = false;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ int ret;
+
+ ret = fsl_sai_set_dai_tdm_slot_tx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+ if (ret)
+ return ret;
- sai->slots = slots;
- sai->slot_width = slot_width;
+ return fsl_sai_set_dai_tdm_slot_rx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+}
+static int fsl_sai_xlate_tdm_slot_mask(unsigned int slots,
+ unsigned int *tx_mask, unsigned int *rx_mask)
+{
+ /* Leave it empty, don't change the value of tx_mask and rx_mask */
return 0;
}
@@ -238,22 +270,22 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
if (dir == SND_SOC_CLOCK_IN)
return 0;
- if (freq > 0 && clk_id != FSL_SAI_CLK_BUS) {
- if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
- dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
- return -EINVAL;
- }
+ if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
+ dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
+ return -EINVAL;
+ }
- if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
- dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
- return -EINVAL;
- }
+ if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
+ dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
+ return -EINVAL;
+ }
- if (sai->mclk_streams == 0) {
- ret = fsl_sai_set_mclk_rate(cpu_dai, clk_id, freq);
- if (ret < 0)
- return ret;
- }
+ if (sai->mclk_streams == 0 && freq > 0) {
+ ret = fsl_sai_set_mclk_rate(cpu_dai,
+ clk_id ? clk_id : FSL_SAI_CLK_MAST1,
+ freq);
+ if (ret < 0)
+ return ret;
}
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
@@ -280,7 +312,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr4 |= FSL_SAI_CR4_MF;
sai->is_pdm_mode = false;
- sai->is_dsp_mode = false;
+ sai->is_dsp_mode[tx] = false;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -309,7 +341,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
*/
val_cr2 |= FSL_SAI_CR2_BCP;
val_cr4 |= FSL_SAI_CR4_FSE;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
break;
case SND_SOC_DAIFMT_DSP_B:
/*
@@ -317,7 +349,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
break;
case SND_SOC_DAIFMT_PDM:
val_cr2 |= FSL_SAI_CR2_BCP;
@@ -357,18 +389,18 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_BP_FP:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
- sai->is_consumer_mode = false;
+ sai->is_consumer_mode[tx] = false;
break;
case SND_SOC_DAIFMT_BC_FC:
- sai->is_consumer_mode = true;
+ sai->is_consumer_mode[tx] = true;
break;
case SND_SOC_DAIFMT_BP_FC:
val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
- sai->is_consumer_mode = false;
+ sai->is_consumer_mode[tx] = false;
break;
case SND_SOC_DAIFMT_BC_FP:
val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
- sai->is_consumer_mode = true;
+ sai->is_consumer_mode[tx] = true;
break;
default:
return -EINVAL;
@@ -400,6 +432,16 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
return ret;
}
+static int fsl_sai_set_dai_fmt_tx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, true);
+}
+
+static int fsl_sai_set_dai_fmt_rx(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ return fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, false);
+}
+
static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai);
@@ -412,7 +454,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
bool support_1_1_ratio = sai->verid.version >= 0x0301;
/* Don't apply to consumer mode */
- if (sai->is_consumer_mode)
+ if (sai->is_consumer_mode[tx])
return 0;
/*
@@ -531,11 +573,11 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
u32 watermark;
int ret, i;
- if (sai->slot_width)
- slot_width = sai->slot_width;
+ if (sai->slot_width[tx])
+ slot_width = sai->slot_width[tx];
- if (sai->slots)
- slots = sai->slots;
+ if (sai->slots[tx])
+ slots = sai->slots[tx];
else if (sai->bclk_ratio)
slots = sai->bclk_ratio / slot_width;
@@ -575,7 +617,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
}
- if (!sai->is_consumer_mode) {
+ if (!sai->is_consumer_mode[tx]) {
ret = fsl_sai_set_bclk(cpu_dai, tx, bclk);
if (ret)
return ret;
@@ -590,7 +632,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
}
- if (!sai->is_dsp_mode && !sai->is_pdm_mode)
+ if (!sai->is_dsp_mode[tx] && !sai->is_pdm_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
@@ -603,6 +645,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+ /* Set to avoid channel swap */
+ val_cr4 |= FSL_SAI_CR4_FCONT;
+
/* Set to output mode to avoid tri-stated data pins */
if (tx)
val_cr4 |= FSL_SAI_CR4_CHMOD;
@@ -613,7 +658,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
* RCR5(TCR5) for playback(capture), or there will be sync error.
*/
- if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) {
+ if (!sai->is_consumer_mode[tx] && fsl_sai_dir_is_synced(sai, adir)) {
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
FSL_SAI_CR4_CHMOD_MASK,
@@ -683,13 +728,13 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
* FSD_MSTR bit for this specific case.
*/
if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
- !sai->is_consumer_mode)
+ !sai->is_consumer_mode[tx])
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FSD_MSTR, 0);
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK |
- FSL_SAI_CR4_CHMOD_MASK,
+ FSL_SAI_CR4_CHMOD_MASK | FSL_SAI_CR4_FCONT_MASK,
val_cr4);
regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs),
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
@@ -697,7 +742,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
/* Enable FSD_MSTR after configuring word width */
if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output &&
- !sai->is_consumer_mode)
+ !sai->is_consumer_mode[tx])
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs),
FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR);
@@ -720,8 +765,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
FSL_SAI_CR3_TRCE_MASK, 0);
- if (!sai->is_consumer_mode &&
- sai->mclk_streams & BIT(substream->stream)) {
+ if (!sai->is_consumer_mode[tx] &&
+ sai->mclk_streams & BIT(substream->stream)) {
clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
sai->mclk_streams &= ~BIT(substream->stream);
}
@@ -759,7 +804,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
* This is a hardware bug, and will be fix in the
* next sai version.
*/
- if (!sai->is_consumer_mode) {
+ if (!sai->is_consumer_mode[tx]) {
/* Software Reset */
regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
/* Clear SR bit to finish the reset */
@@ -872,7 +917,7 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
+ SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates);
return ret;
}
@@ -914,6 +959,32 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.startup = fsl_sai_startup,
};
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = {
+ .probe = fsl_sai_dai_probe,
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
+ .set_sysclk = fsl_sai_set_dai_sysclk,
+ .set_fmt = fsl_sai_set_dai_fmt_tx,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_tx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
+ .hw_params = fsl_sai_hw_params,
+ .hw_free = fsl_sai_hw_free,
+ .trigger = fsl_sai_trigger,
+ .startup = fsl_sai_startup,
+};
+
+static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = {
+ .probe = fsl_sai_dai_probe,
+ .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
+ .set_sysclk = fsl_sai_set_dai_sysclk,
+ .set_fmt = fsl_sai_set_dai_fmt_rx,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_rx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
+ .hw_params = fsl_sai_hw_params,
+ .hw_free = fsl_sai_hw_free,
+ .trigger = fsl_sai_trigger,
+ .startup = fsl_sai_startup,
+};
+
static int fsl_sai_dai_resume(struct snd_soc_component *component)
{
struct fsl_sai *sai = snd_soc_component_get_drvdata(component);
@@ -931,26 +1002,55 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component)
return 0;
}
-static struct snd_soc_dai_driver fsl_sai_dai_template = {
- .playback = {
- .stream_name = "CPU-Playback",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 2822400,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_SAI_FORMATS,
+static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
+ {
+ .name = "sai-tx-rx",
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_ops,
+ },
+ {
+ .name = "sai-tx",
+ .playback = {
+ .stream_name = "SAI-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_tx_ops,
},
- .capture = {
- .stream_name = "CPU-Capture",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 2822400,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_SAI_FORMATS,
+ {
+ .name = "sai-rx",
+ .capture = {
+ .stream_name = "SAI-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 2822400,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_SAI_FORMATS,
+ },
+ .ops = &fsl_sai_pcm_dai_rx_ops,
},
- .ops = &fsl_sai_pcm_dai_ops,
};
static const struct snd_soc_component_driver fsl_component = {
@@ -1376,6 +1476,11 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
&sai->pll11k_clk);
+ fsl_asoc_constrain_rates(&sai->constraint_rates,
+ &fsl_sai_rate_constraints,
+ sai->pll8k_clk, sai->pll11k_clk, NULL,
+ sai->constraint_rates_list);
+
/* Use Multi FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)
@@ -1399,15 +1504,15 @@ static int fsl_sai_probe(struct platform_device *pdev)
return ret;
}
- memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template,
- sizeof(fsl_sai_dai_template));
+ memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template,
+ sizeof(*fsl_sai_dai_template) * ARRAY_SIZE(fsl_sai_dai_template));
/* Sync Tx with Rx as default by following old DT binding */
sai->synchronous[RX] = true;
sai->synchronous[TX] = false;
- sai->cpu_dai_drv.symmetric_rate = 1;
- sai->cpu_dai_drv.symmetric_channels = 1;
- sai->cpu_dai_drv.symmetric_sample_bits = 1;
+ sai->cpu_dai_drv[0].symmetric_rate = 1;
+ sai->cpu_dai_drv[0].symmetric_channels = 1;
+ sai->cpu_dai_drv[0].symmetric_sample_bits = 1;
if (of_property_read_bool(np, "fsl,sai-synchronous-rx") &&
of_property_read_bool(np, "fsl,sai-asynchronous")) {
@@ -1424,9 +1529,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
/* Discard all settings for asynchronous mode */
sai->synchronous[RX] = false;
sai->synchronous[TX] = false;
- sai->cpu_dai_drv.symmetric_rate = 0;
- sai->cpu_dai_drv.symmetric_channels = 0;
- sai->cpu_dai_drv.symmetric_sample_bits = 0;
+ sai->cpu_dai_drv[0].symmetric_rate = 0;
+ sai->cpu_dai_drv[0].symmetric_channels = 0;
+ sai->cpu_dai_drv[0].symmetric_sample_bits = 0;
}
sai->mclk_direction_output = of_property_read_bool(np, "fsl,sai-mclk-direction-output");
@@ -1505,7 +1610,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
}
ret = devm_snd_soc_register_component(dev, &fsl_component,
- &sai->cpu_dai_drv, 1);
+ sai->cpu_dai_drv, ARRAY_SIZE(fsl_sai_dai_template));
if (ret)
goto err_pm_get_sync;
@@ -1746,18 +1851,16 @@ disable_bus_clk:
}
static const struct dev_pm_ops fsl_sai_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend,
- fsl_sai_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_sai_runtime_suspend, fsl_sai_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_sai_driver = {
.probe = fsl_sai_probe,
- .remove_new = fsl_sai_remove,
+ .remove = fsl_sai_remove,
.driver = {
.name = "fsl-sai",
- .pm = &fsl_sai_pm_ops,
+ .pm = pm_ptr(&fsl_sai_pm_ops),
.of_match_table = fsl_sai_ids,
},
};
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 550df87b6a06..6c917f79c6b0 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -9,6 +9,7 @@
#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
+#define FAL_SAI_NUM_RATES 20
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
@@ -137,6 +138,7 @@
/* SAI Transmit and Receive Configuration 4 Register */
+#define FSL_SAI_CR4_FCONT_MASK BIT(28)
#define FSL_SAI_CR4_FCONT BIT(28)
#define FSL_SAI_CR4_FCOMB_SHIFT BIT(26)
#define FSL_SAI_CR4_FCOMB_SOFT BIT(27)
@@ -282,9 +284,9 @@ struct fsl_sai {
struct clk *pll11k_clk;
struct resource *res;
- bool is_consumer_mode;
+ bool is_consumer_mode[2];
bool is_lsb_first;
- bool is_dsp_mode;
+ bool is_dsp_mode[2];
bool is_pdm_mode;
bool is_multi_fifo_dma;
bool synchronous[2];
@@ -294,12 +296,12 @@ struct fsl_sai {
unsigned int mclk_id[2];
unsigned int mclk_streams;
- unsigned int slots;
- unsigned int slot_width;
+ unsigned int slots[2];
+ unsigned int slot_width[2];
unsigned int bclk_ratio;
const struct fsl_sai_soc_data *soc_data;
- struct snd_soc_dai_driver cpu_dai_drv;
+ struct snd_soc_dai_driver cpu_dai_drv[3];
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct fsl_sai_verid verid;
@@ -308,6 +310,8 @@ struct fsl_sai {
struct pinctrl *pinctrl;
struct pinctrl_state *pins_state;
struct sdma_peripheral_config audio_config[2];
+ struct snd_pcm_hw_constraint_list constraint_rates;
+ unsigned int constraint_rates_list[FAL_SAI_NUM_RATES];
};
#define TX 1
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index a63121c888e0..ee946e0d3f49 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -1204,7 +1204,7 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
},
/* DPLL lock info get controller */
{
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = RX_SAMPLE_RATE_KCONTROL,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -1667,7 +1667,6 @@ static void fsl_spdif_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#ifdef CONFIG_PM
static int fsl_spdif_runtime_suspend(struct device *dev)
{
struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
@@ -1739,13 +1738,11 @@ disable_core_clk:
return ret;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops fsl_spdif_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume,
+ NULL)
};
static const struct of_device_id fsl_spdif_dt_ids[] = {
@@ -1763,10 +1760,10 @@ static struct platform_driver fsl_spdif_driver = {
.driver = {
.name = "fsl-spdif-dai",
.of_match_table = fsl_spdif_dt_ids,
- .pm = &fsl_spdif_pm,
+ .pm = pm_ptr(&fsl_spdif_pm),
},
.probe = fsl_spdif_probe,
- .remove_new = fsl_spdif_remove,
+ .remove = fsl_spdif_remove,
};
module_platform_driver(fsl_spdif_driver);
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index ab6ec1974807..320108bebf30 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1401,8 +1401,10 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev,
goto error_pcm;
} else {
ret = imx_pcm_dma_init(pdev);
- if (ret)
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to init PCM DMA\n");
goto error_pcm;
+ }
}
return 0;
@@ -1691,7 +1693,6 @@ static void fsl_ssi_remove(struct platform_device *pdev)
}
}
-#ifdef CONFIG_PM_SLEEP
static int fsl_ssi_suspend(struct device *dev)
{
struct fsl_ssi *ssi = dev_get_drvdata(dev);
@@ -1721,20 +1722,19 @@ static int fsl_ssi_resume(struct device *dev)
return regcache_sync(regs);
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_ssi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+ SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
};
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
.of_match_table = fsl_ssi_ids,
- .pm = &fsl_ssi_pm,
+ .pm = pm_sleep_ptr(&fsl_ssi_pm),
},
.probe = fsl_ssi_probe,
- .remove_new = fsl_ssi_remove,
+ .remove = fsl_ssi_remove,
};
module_platform_driver(fsl_ssi_driver);
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
index a5ab27c2f711..d69a6b9795bf 100644
--- a/sound/soc/fsl/fsl_utils.c
+++ b/sound/soc/fsl/fsl_utils.c
@@ -152,6 +152,51 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
}
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
+/**
+ * fsl_asoc_constrain_rates - constrain rates according to clocks
+ *
+ * @target_constr: target constraint
+ * @original_constr: original constraint
+ * @pll8k_clk: PLL clock pointer for 8kHz
+ * @pll11k_clk: PLL clock pointer for 11kHz
+ * @ext_clk: External clock pointer
+ * @target_rates: target rates array
+ *
+ * This function constrain rates according to clocks
+ */
+void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
+ const struct snd_pcm_hw_constraint_list *original_constr,
+ struct clk *pll8k_clk, struct clk *pll11k_clk,
+ struct clk *ext_clk, int *target_rates)
+{
+ int i, j, k = 0;
+ u64 clk_rate[3];
+
+ *target_constr = *original_constr;
+ if (pll8k_clk || pll11k_clk || ext_clk) {
+ target_constr->list = target_rates;
+ target_constr->count = 0;
+ for (i = 0; i < original_constr->count; i++) {
+ clk_rate[0] = clk_get_rate(pll8k_clk);
+ clk_rate[1] = clk_get_rate(pll11k_clk);
+ clk_rate[2] = clk_get_rate(ext_clk);
+ for (j = 0; j < 3; j++) {
+ if (clk_rate[j] != 0 &&
+ do_div(clk_rate[j], original_constr->list[i]) == 0) {
+ target_rates[k++] = original_constr->list[i];
+ target_constr->count++;
+ break;
+ }
+ }
+ }
+
+ /* protection for if there is no proper rate found*/
+ if (!target_constr->count)
+ *target_constr = *original_constr;
+ }
+}
+EXPORT_SYMBOL(fsl_asoc_constrain_rates);
+
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h
index 4d5f3d93bc81..21b25a11ecda 100644
--- a/sound/soc/fsl/fsl_utils.h
+++ b/sound/soc/fsl/fsl_utils.h
@@ -26,4 +26,9 @@ void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,
void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
struct clk *pll8k_clk,
struct clk *pll11k_clk, u64 ratio);
+
+void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
+ const struct snd_pcm_hw_constraint_list *original_constr,
+ struct clk *pll8k_clk, struct clk *pll11k_clk,
+ struct clk *ext_clk, int *target_rates);
#endif /* _FSL_UTILS_H */
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index c46f64557a7f..e3111dd80be4 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -15,24 +15,37 @@
#include <sound/pcm_params.h>
#include "fsl_xcvr.h"
+#include "fsl_utils.h"
#include "imx-pcm.h"
#define FSL_XCVR_CAPDS_SIZE 256
+#define SPDIF_NUM_RATES 7
+
+enum fsl_xcvr_pll_verison {
+ PLL_MX8MP,
+ PLL_MX95,
+};
struct fsl_xcvr_soc_data {
const char *fw_name;
bool spdif_only;
bool use_edma;
+ bool use_phy;
+ enum fsl_xcvr_pll_verison pll_ver;
};
struct fsl_xcvr {
const struct fsl_xcvr_soc_data *soc_data;
struct platform_device *pdev;
struct regmap *regmap;
+ struct regmap *regmap_phy;
+ struct regmap *regmap_pll;
struct clk *ipg_clk;
struct clk *pll_ipg_clk;
struct clk *phy_clk;
struct clk *spba_clk;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
struct reset_control *reset;
u8 streams;
u32 mode;
@@ -43,6 +56,10 @@ struct fsl_xcvr {
struct snd_aes_iec958 rx_iec958;
struct snd_aes_iec958 tx_iec958;
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
+ struct work_struct work_rst;
+ spinlock_t lock; /* Protect hw_reset and trigger */
+ struct snd_pcm_hw_constraint_list spdif_constr_rates;
+ u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
};
static const struct fsl_xcvr_pll_conf {
@@ -159,7 +176,7 @@ static int fsl_xcvr_capds_put(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capabilities Data Structure",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = fsl_xcvr_type_capds_bytes_info,
@@ -176,7 +193,7 @@ static int fsl_xcvr_activate_ctl(struct snd_soc_dai *dai, const char *name,
lockdep_assert_held(&card->snd_card->controls_rwsem);
- kctl = snd_soc_card_get_kcontrol_locked(card, name);
+ kctl = snd_soc_card_get_kcontrol(card, name);
if (kctl == NULL)
return -ENOENT;
@@ -245,7 +262,7 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
idx = BIT(phy ? 26 : 24);
tidx = BIT(phy ? 27 : 25);
- regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_WDATA, data);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
@@ -259,13 +276,66 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
return ret;
}
+static int fsl_xcvr_ai_read(struct fsl_xcvr *xcvr, u8 reg, u32 *data, bool phy)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ u32 val, idx, tidx;
+ int ret;
+
+ idx = BIT(phy ? 26 : 24);
+ tidx = BIT(phy ? 27 : 25);
+
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
+
+ ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val,
+ (val & idx) == ((val & tidx) >> 1),
+ 10, 10000);
+ if (ret)
+ dev_err(dev, "AI timeout: failed to read %s reg 0x%02x\n",
+ phy ? "PHY" : "PLL", reg);
+
+ regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_RDATA, data);
+
+ return ret;
+}
+
+static int fsl_xcvr_phy_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_read(xcvr, reg, val, 1);
+}
+
+static int fsl_xcvr_phy_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_write(xcvr, reg, val, 1);
+}
+
+static int fsl_xcvr_pll_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_read(xcvr, reg, val, 0);
+}
+
+static int fsl_xcvr_pll_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct fsl_xcvr *xcvr = context;
+
+ return fsl_xcvr_ai_write(xcvr, reg, val, 0);
+}
+
static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
{
struct device *dev = &xcvr->pdev->dev;
- u32 i, div = 0, log2;
+ u32 i, div = 0, log2, val;
int ret;
- if (xcvr->soc_data->spdif_only)
+ if (!xcvr->soc_data->use_phy)
return 0;
for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) {
@@ -288,65 +358,82 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
return ret;
}
- /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET,
- FSL_XCVR_PLL_BANDGAP_EN_VBG, 0);
-
- /* PLL: CTRL0: DIV_INTEGER */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0);
- /* PLL: NUMERATOR: MFN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0);
- /* PLL: DENOMINATOR: MFD */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0);
- /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
- FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0);
- udelay(25);
- /* PLL: CTRL0: Clear Hold Ring Off */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR,
- FSL_XCVR_PLL_CTRL0_HROFF, 0);
- udelay(100);
- if (tx) { /* TX is enabled for SPDIF only */
- /* PLL: POSTDIV: PDIV0 */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
- FSL_XCVR_PLL_PDIVx(log2, 0), 0);
- /* PLL: CTRL_SET: CLKMUX0_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
- FSL_XCVR_PLL_CTRL0_CM0_EN, 0);
- } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */
- /* PLL: POSTDIV: PDIV1 */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
- FSL_XCVR_PLL_PDIVx(log2, 1), 0);
- /* PLL: CTRL_SET: CLKMUX1_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
- FSL_XCVR_PLL_CTRL0_CM1_EN, 0);
- } else { /* SPDIF / ARC RX */
- /* PLL: POSTDIV: PDIV2 */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
- FSL_XCVR_PLL_PDIVx(log2, 2), 0);
- /* PLL: CTRL_SET: CLKMUX2_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
- FSL_XCVR_PLL_CTRL0_CM2_EN, 0);
+ switch (xcvr->soc_data->pll_ver) {
+ case PLL_MX8MP:
+ /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_BANDGAP,
+ FSL_XCVR_PLL_BANDGAP_EN_VBG);
+
+ /* PLL: CTRL0: DIV_INTEGER */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi);
+ /* PLL: NUMERATOR: MFN */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn);
+ /* PLL: DENOMINATOR: MFD */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd);
+ /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP);
+ udelay(25);
+ /* PLL: CTRL0: Clear Hold Ring Off */
+ regmap_clear_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_HROFF);
+ udelay(100);
+ if (tx) { /* TX is enabled for SPDIF only */
+ /* PLL: POSTDIV: PDIV0 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 0));
+ /* PLL: CTRL_SET: CLKMUX0_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM0_EN);
+ } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */
+ /* PLL: POSTDIV: PDIV1 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 1));
+ /* PLL: CTRL_SET: CLKMUX1_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM1_EN);
+ } else { /* SPDIF / ARC RX */
+ /* PLL: POSTDIV: PDIV2 */
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
+ FSL_XCVR_PLL_PDIVx(log2, 2));
+ /* PLL: CTRL_SET: CLKMUX2_EN */
+ regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
+ FSL_XCVR_PLL_CTRL0_CM2_EN);
+ }
+ break;
+ case PLL_MX95:
+ val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DIV, val);
+ val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_NUMERATOR, val);
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DENOMINATOR,
+ fsl_xcvr_pll_cfg[i].mfd);
+ val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN;
+ regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_CTRL, val);
+ break;
+ default:
+ dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
+ return -EINVAL;
}
if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
- FSL_XCVR_PHY_CTRL_TSDIFF_OE |
- FSL_XCVR_PHY_CTRL_PHY_EN, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TSDIFF_OE |
+ FSL_XCVR_PHY_CTRL_PHY_EN);
/* PHY: CTRL2_SET: EARC_TX_MODE */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET,
- FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
+ FSL_XCVR_PHY_CTRL2_EARC_TXMS);
} else if (!tx) { /* SPDIF / ARC RX mode */
if (xcvr->mode == FSL_XCVR_MODE_SPDIF)
/* PHY: CTRL_SET: SPDIF_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
- FSL_XCVR_PHY_CTRL_SPDIF_EN, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_SPDIF_EN);
else /* PHY: CTRL_SET: ARC RX setup */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
- FSL_XCVR_PHY_CTRL_PHY_EN |
- FSL_XCVR_PHY_CTRL_RX_CM_EN |
- fsl_xcvr_phy_arc_cfg[xcvr->arc_mode], 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_PHY_EN |
+ FSL_XCVR_PHY_CTRL_RX_CM_EN |
+ fsl_xcvr_phy_arc_cfg[xcvr->arc_mode]);
}
dev_dbg(dev, "PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u\n",
@@ -362,6 +449,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
freq = xcvr->soc_data->spdif_only ? freq / 5 : freq;
clk_disable_unprepare(xcvr->phy_clk);
+ fsl_asoc_reparent_pll_clocks(dev, xcvr->phy_clk,
+ xcvr->pll8k_clk, xcvr->pll11k_clk, freq);
ret = clk_set_rate(xcvr->phy_clk, freq);
if (ret < 0) {
dev_err(dev, "Error while setting AUD PLL rate: %d\n", ret);
@@ -373,7 +462,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
return ret;
}
- if (xcvr->soc_data->spdif_only)
+ if (!xcvr->soc_data->use_phy)
return 0;
/* Release AI interface from reset */
ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
@@ -385,17 +474,17 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
- FSL_XCVR_PHY_CTRL_TSDIFF_OE |
- FSL_XCVR_PHY_CTRL_PHY_EN, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TSDIFF_OE |
+ FSL_XCVR_PHY_CTRL_PHY_EN);
/* PHY: CTRL2_SET: EARC_TX_MODE */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET,
- FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
+ FSL_XCVR_PHY_CTRL2_EARC_TXMS);
} else { /* SPDIF mode */
/* PHY: CTRL_SET: TX_CLK_AUD_SS | SPDIF_EN */
- fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
- FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
- FSL_XCVR_PHY_CTRL_SPDIF_EN, 1);
+ regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
+ FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
+ FSL_XCVR_PHY_CTRL_SPDIF_EN);
}
dev_dbg(dev, "PLL Fexp: %u\n", freq);
@@ -417,7 +506,7 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
if (xcvr->soc_data->spdif_only && tx) {
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET,
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM,
FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM);
if (ret < 0) {
@@ -435,8 +524,8 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
return ret;
}
- ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET,
- FSL_XCVR_TX_DPTH_CTRL_FRM_FMT);
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_FRM_FMT);
if (ret < 0) {
dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret);
return ret;
@@ -453,11 +542,11 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
* Clear RX FIFO, flip RX FIFO bits,
* disable eARC related HW mode detects
*/
- ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET,
- FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
- FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
- FSL_XCVR_RX_DPTH_CTRL_COMP |
- FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
+ FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
+ FSL_XCVR_RX_DPTH_CTRL_COMP |
+ FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
if (ret < 0) {
dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
return ret;
@@ -474,18 +563,18 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
case FSL_XCVR_MODE_EARC:
if (!tx) {
/** Clear RX FIFO, flip RX FIFO bits */
- ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET,
- FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
- FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
+ ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
+ FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
if (ret < 0) {
dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
return ret;
}
/** Enable eARC related HW mode detects */
- ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_CLR,
- FSL_XCVR_RX_DPTH_CTRL_COMP |
- FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
+ ret = regmap_clear_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
+ FSL_XCVR_RX_DPTH_CTRL_COMP |
+ FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
if (ret < 0) {
dev_err(dai->dev, "Failed to clr TX_DPTH: %d\n", ret);
return ret;
@@ -500,16 +589,6 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
break;
}
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
- FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
- if (ret < 0) {
- dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
- return ret;
- }
-
- /* set DPATH RESET */
- m_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx);
- v_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx);
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, m_ctl, v_ctl);
if (ret < 0) {
dev_err(dai->dev, "Error while setting EXT_CTRL: %d\n", ret);
@@ -564,8 +643,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
case FSL_XCVR_MODE_ARC:
- ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
- &fsl_xcvr_spdif_rates_constr);
+ if (xcvr->soc_data->spdif_only && tx)
+ ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
+ &xcvr->spdif_constr_rates);
+ else
+ ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
+ &fsl_xcvr_spdif_rates_constr);
break;
case FSL_XCVR_MODE_EARC:
ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr,
@@ -644,12 +727,24 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- int ret;
+ unsigned long lock_flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&xcvr->lock, lock_flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* set DPATH RESET */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DPTH_RESET(tx),
+ FSL_XCVR_EXT_CTRL_DPTH_RESET(tx));
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set DPATH RESET: %d\n", ret);
+ goto release_lock;
+ }
+
if (tx) {
switch (xcvr->mode) {
case FSL_XCVR_MODE_EARC:
@@ -659,16 +754,16 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_XCVR_ISR_CMDC_TX_EN);
if (ret < 0) {
dev_err(dai->dev, "err updating isr %d\n", ret);
- return ret;
+ goto release_lock;
}
fallthrough;
case FSL_XCVR_MODE_SPDIF:
- ret = regmap_write(xcvr->regmap,
- FSL_XCVR_TX_DPTH_CTRL_SET,
- FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
+ ret = regmap_set_bits(xcvr->regmap,
+ FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
- return ret;
+ goto release_lock;
}
break;
}
@@ -679,7 +774,14 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0);
if (ret < 0) {
dev_err(dai->dev, "Failed to enable DMA: %d\n", ret);
- return ret;
+ goto release_lock;
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
+ FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL);
+ if (ret < 0) {
+ dev_err(dai->dev, "Error while setting IER0: %d\n", ret);
+ goto release_lock;
}
/* clear DPATH RESET */
@@ -688,7 +790,7 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
0);
if (ret < 0) {
dev_err(dai->dev, "Failed to clear DPATH RESET: %d\n", ret);
- return ret;
+ goto release_lock;
}
break;
@@ -701,18 +803,25 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_XCVR_EXT_CTRL_DMA_DIS(tx));
if (ret < 0) {
dev_err(dai->dev, "Failed to disable DMA: %d\n", ret);
- return ret;
+ goto release_lock;
+ }
+
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
+ FSL_XCVR_IRQ_EARC_ALL, 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to clear IER0: %d\n", ret);
+ goto release_lock;
}
if (tx) {
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
- ret = regmap_write(xcvr->regmap,
- FSL_XCVR_TX_DPTH_CTRL_CLR,
- FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
+ ret = regmap_clear_bits(xcvr->regmap,
+ FSL_XCVR_TX_DPTH_CTRL,
+ FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
- return ret;
+ goto release_lock;
}
if (xcvr->soc_data->spdif_only)
break;
@@ -726,17 +835,20 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
if (ret < 0) {
dev_err(dai->dev,
"Err updating ISR %d\n", ret);
- return ret;
+ goto release_lock;
}
break;
}
}
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
- return 0;
+release_lock:
+ spin_unlock_irqrestore(&xcvr->lock, lock_flags);
+ return ret;
}
static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr)
@@ -1017,7 +1129,7 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
- if (xcvr->soc_data->spdif_only)
+ if (!xcvr->soc_data->use_phy)
if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) ||
reg > FSL_XCVR_TX_DPTH_BCRR)
return false;
@@ -1090,7 +1202,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
- if (xcvr->soc_data->spdif_only)
+ if (!xcvr->soc_data->use_phy)
if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA)
return false;
switch (reg) {
@@ -1119,6 +1231,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_CTRL:
case FSL_XCVR_TX_DPTH_CTRL_SET:
case FSL_XCVR_TX_DPTH_CTRL_CLR:
case FSL_XCVR_TX_DPTH_CTRL_TOG:
@@ -1140,7 +1253,49 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
static bool fsl_xcvr_volatile_reg(struct device *dev, unsigned int reg)
{
- return fsl_xcvr_readable_reg(dev, reg);
+ switch (reg) {
+ case FSL_XCVR_EXT_STATUS:
+ case FSL_XCVR_EXT_ISR:
+ case FSL_XCVR_EXT_ISR_SET:
+ case FSL_XCVR_EXT_ISR_CLR:
+ case FSL_XCVR_EXT_ISR_TOG:
+ case FSL_XCVR_ISR:
+ case FSL_XCVR_ISR_SET:
+ case FSL_XCVR_ISR_CLR:
+ case FSL_XCVR_ISR_TOG:
+ case FSL_XCVR_PHY_AI_CTRL:
+ case FSL_XCVR_PHY_AI_CTRL_SET:
+ case FSL_XCVR_PHY_AI_CTRL_CLR:
+ case FSL_XCVR_PHY_AI_CTRL_TOG:
+ case FSL_XCVR_PHY_AI_RDATA:
+ case FSL_XCVR_RX_CS_DATA_0:
+ case FSL_XCVR_RX_CS_DATA_1:
+ case FSL_XCVR_RX_CS_DATA_2:
+ case FSL_XCVR_RX_CS_DATA_3:
+ case FSL_XCVR_RX_CS_DATA_4:
+ case FSL_XCVR_RX_CS_DATA_5:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_RX_DPTH_TSCR:
+ case FSL_XCVR_RX_DPTH_BCR:
+ case FSL_XCVR_RX_DPTH_BCTR:
+ case FSL_XCVR_RX_DPTH_BCRR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
+ case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
+ case FSL_XCVR_TX_DPTH_TSCR:
+ case FSL_XCVR_TX_DPTH_BCR:
+ case FSL_XCVR_TX_DPTH_BCTR:
+ case FSL_XCVR_TX_DPTH_BCRR:
+ case FSL_XCVR_DEBUG_REG_0:
+ case FSL_XCVR_DEBUG_REG_1:
+ return true;
+ default:
+ return false;
+ }
}
static const struct regmap_config fsl_xcvr_regmap_cfg = {
@@ -1156,6 +1311,77 @@ static const struct regmap_config fsl_xcvr_regmap_cfg = {
.cache_type = REGCACHE_FLAT,
};
+static const struct reg_default fsl_xcvr_phy_reg_defaults[] = {
+ { FSL_XCVR_PHY_CTRL, 0x58200804 },
+ { FSL_XCVR_PHY_STATUS, 0x00000000 },
+ { FSL_XCVR_PHY_ANALOG_TRIM, 0x00260F13 },
+ { FSL_XCVR_PHY_SLEW_RATE_TRIM, 0x00000411 },
+ { FSL_XCVR_PHY_DATA_TEST_DELAY, 0x00990000 },
+ { FSL_XCVR_PHY_TEST_CTRL, 0x00000000 },
+ { FSL_XCVR_PHY_DIFF_CDR_CTRL, 0x016D0009 },
+ { FSL_XCVR_PHY_CTRL2, 0x80000000 },
+};
+
+static const struct regmap_config fsl_xcvr_regmap_phy_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_PHY_CTRL2_TOG,
+ .reg_defaults = fsl_xcvr_phy_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_xcvr_phy_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_phy_reg_read,
+ .reg_write = fsl_xcvr_phy_reg_write,
+};
+
+static const struct regmap_config fsl_xcvr_regmap_pllv0_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_PLL_STAT0_TOG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_pll_reg_read,
+ .reg_write = fsl_xcvr_pll_reg_write,
+};
+
+static const struct regmap_config fsl_xcvr_regmap_pllv1_cfg = {
+ .reg_bits = 8,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_XCVR_GP_PLL_STATUS_TOG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_read = fsl_xcvr_pll_reg_read,
+ .reg_write = fsl_xcvr_pll_reg_write,
+};
+
+static void reset_rx_work(struct work_struct *work)
+{
+ struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst);
+ struct device *dev = &xcvr->pdev->dev;
+ unsigned long lock_flags;
+ u32 ext_ctrl;
+
+ dev_dbg(dev, "reset rx path\n");
+ spin_lock_irqsave(&xcvr->lock, lock_flags);
+ regmap_read(xcvr->regmap, FSL_XCVR_EXT_CTRL, &ext_ctrl);
+
+ if (!(ext_ctrl & FSL_XCVR_EXT_CTRL_DMA_RD_DIS)) {
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_DMA_RD_DIS,
+ 0);
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_RX_DPTH_RESET,
+ 0);
+ }
+ spin_unlock_irqrestore(&xcvr->lock, lock_flags);
+}
+
static irqreturn_t irq0_isr(int irq, void *devid)
{
struct fsl_xcvr *xcvr = (struct fsl_xcvr *)devid;
@@ -1223,6 +1449,33 @@ static irqreturn_t irq0_isr(int irq, void *devid)
dev_dbg(dev, "DMA write request\n");
isr_clr |= FSL_XCVR_IRQ_DMA_WR_REQ;
}
+ if (isr & FSL_XCVR_IRQ_CMDC_STATUS_UPD) {
+ dev_dbg(dev, "CMDC status update\n");
+ isr_clr |= FSL_XCVR_IRQ_CMDC_STATUS_UPD;
+ }
+ if (isr & FSL_XCVR_IRQ_PREAMBLE_MISMATCH) {
+ dev_dbg(dev, "Preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_PREAMBLE_MISMATCH;
+ }
+ if (isr & FSL_XCVR_IRQ_UNEXP_PRE_REC) {
+ dev_dbg(dev, "Unexpected preamble received\n");
+ isr_clr |= FSL_XCVR_IRQ_UNEXP_PRE_REC;
+ }
+ if (isr & FSL_XCVR_IRQ_M_W_PRE_MISMATCH) {
+ dev_dbg(dev, "M/W preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_M_W_PRE_MISMATCH;
+ }
+ if (isr & FSL_XCVR_IRQ_B_PRE_MISMATCH) {
+ dev_dbg(dev, "B preamble mismatch\n");
+ isr_clr |= FSL_XCVR_IRQ_B_PRE_MISMATCH;
+ }
+
+ if (isr & (FSL_XCVR_IRQ_PREAMBLE_MISMATCH |
+ FSL_XCVR_IRQ_UNEXP_PRE_REC |
+ FSL_XCVR_IRQ_M_W_PRE_MISMATCH |
+ FSL_XCVR_IRQ_B_PRE_MISMATCH)) {
+ schedule_work(&xcvr->work_rst);
+ }
if (isr_clr) {
regmap_write(regmap, FSL_XCVR_EXT_ISR_CLR, isr_clr);
@@ -1234,6 +1487,8 @@ static irqreturn_t irq0_isr(int irq, void *devid)
static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = {
.fw_name = "imx/xcvr/xcvr-imx8mp.bin",
+ .use_phy = true,
+ .pll_ver = PLL_MX8MP,
};
static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = {
@@ -1241,9 +1496,17 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = {
.use_edma = true,
};
+static const struct fsl_xcvr_soc_data fsl_xcvr_imx95_data = {
+ .spdif_only = true,
+ .use_phy = true,
+ .use_edma = true,
+ .pll_ver = PLL_MX95,
+};
+
static const struct of_device_id fsl_xcvr_dt_ids[] = {
{ .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data },
{ .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data},
+ { .compatible = "fsl,imx95-xcvr", .data = &fsl_xcvr_imx95_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids);
@@ -1287,6 +1550,18 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return PTR_ERR(xcvr->pll_ipg_clk);
}
+ fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk,
+ &xcvr->pll11k_clk);
+
+ if (xcvr->soc_data->spdif_only) {
+ if (!(xcvr->pll8k_clk || xcvr->pll11k_clk))
+ xcvr->pll8k_clk = xcvr->phy_clk;
+ fsl_asoc_constrain_rates(&xcvr->spdif_constr_rates,
+ &fsl_xcvr_spdif_rates_constr,
+ xcvr->pll8k_clk, xcvr->pll11k_clk, NULL,
+ xcvr->spdif_constr_rates_list);
+ }
+
xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram");
if (IS_ERR(xcvr->ram_addr))
return PTR_ERR(xcvr->ram_addr);
@@ -1303,6 +1578,40 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return PTR_ERR(xcvr->regmap);
}
+ if (xcvr->soc_data->use_phy) {
+ xcvr->regmap_phy = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_phy_cfg);
+ if (IS_ERR(xcvr->regmap_phy)) {
+ dev_err(dev, "failed to init XCVR PHY regmap: %ld\n",
+ PTR_ERR(xcvr->regmap_phy));
+ return PTR_ERR(xcvr->regmap_phy);
+ }
+
+ switch (xcvr->soc_data->pll_ver) {
+ case PLL_MX8MP:
+ xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_pllv0_cfg);
+ if (IS_ERR(xcvr->regmap_pll)) {
+ dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
+ PTR_ERR(xcvr->regmap_pll));
+ return PTR_ERR(xcvr->regmap_pll);
+ }
+ break;
+ case PLL_MX95:
+ xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
+ &fsl_xcvr_regmap_pllv1_cfg);
+ if (IS_ERR(xcvr->regmap_pll)) {
+ dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
+ PTR_ERR(xcvr->regmap_pll));
+ return PTR_ERR(xcvr->regmap_pll);
+ }
+ break;
+ default:
+ dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
+ return -EINVAL;
+ }
+ }
+
xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(xcvr->reset)) {
dev_err(dev, "failed to get XCVR reset control\n");
@@ -1336,6 +1645,10 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, xcvr);
pm_runtime_enable(dev);
regcache_cache_only(xcvr->regmap, true);
+ if (xcvr->soc_data->use_phy) {
+ regcache_cache_only(xcvr->regmap_phy, true);
+ regcache_cache_only(xcvr->regmap_pll, true);
+ }
/*
* Register platform component before registering cpu dai for there
@@ -1356,30 +1669,26 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
fsl_xcvr_comp.name);
}
+ INIT_WORK(&xcvr->work_rst, reset_rx_work);
+ spin_lock_init(&xcvr->lock);
return ret;
}
static void fsl_xcvr_remove(struct platform_device *pdev)
{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
+
+ cancel_work_sync(&xcvr->work_rst);
pm_runtime_disable(&pdev->dev);
}
-static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
+static int fsl_xcvr_runtime_suspend(struct device *dev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
int ret;
- /*
- * Clear interrupts, when streams starts or resumes after
- * suspend, interrupts are enabled in prepare(), so no need
- * to enable interrupts in resume().
- */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
- FSL_XCVR_IRQ_EARC_ALL, 0);
- if (ret < 0)
- dev_err(dev, "Failed to clear IER0: %d\n", ret);
-
- if (!xcvr->soc_data->spdif_only) {
+ if (!xcvr->soc_data->spdif_only &&
+ xcvr->mode == FSL_XCVR_MODE_EARC) {
/* Assert M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET,
@@ -1389,6 +1698,10 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
}
regcache_cache_only(xcvr->regmap, true);
+ if (xcvr->soc_data->use_phy) {
+ regcache_cache_only(xcvr->regmap_phy, true);
+ regcache_cache_only(xcvr->regmap_pll, true);
+ }
clk_disable_unprepare(xcvr->spba_clk);
clk_disable_unprepare(xcvr->phy_clk);
@@ -1398,7 +1711,7 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev)
+static int fsl_xcvr_runtime_resume(struct device *dev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
int ret;
@@ -1433,6 +1746,12 @@ static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev)
goto stop_phy_clk;
}
+ ret = reset_control_deassert(xcvr->reset);
+ if (ret) {
+ dev_err(dev, "failed to deassert M0+ reset.\n");
+ goto stop_spba_clk;
+ }
+
regcache_cache_only(xcvr->regmap, false);
regcache_mark_dirty(xcvr->regmap);
ret = regcache_sync(xcvr->regmap);
@@ -1442,31 +1761,49 @@ static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev)
goto stop_spba_clk;
}
- if (xcvr->soc_data->spdif_only)
- return 0;
+ if (xcvr->soc_data->use_phy) {
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
+ FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
+ if (ret < 0) {
+ dev_err(dev, "Error while release PHY reset: %d\n", ret);
+ goto stop_spba_clk;
+ }
- ret = reset_control_deassert(xcvr->reset);
- if (ret) {
- dev_err(dev, "failed to deassert M0+ reset.\n");
- goto stop_spba_clk;
- }
+ regcache_cache_only(xcvr->regmap_phy, false);
+ regcache_mark_dirty(xcvr->regmap_phy);
+ ret = regcache_sync(xcvr->regmap_phy);
+ if (ret) {
+ dev_err(dev, "failed to sync phy regcache.\n");
+ goto stop_spba_clk;
+ }
- ret = fsl_xcvr_load_firmware(xcvr);
- if (ret) {
- dev_err(dev, "failed to load firmware.\n");
- goto stop_spba_clk;
+ regcache_cache_only(xcvr->regmap_pll, false);
+ regcache_mark_dirty(xcvr->regmap_pll);
+ ret = regcache_sync(xcvr->regmap_pll);
+ if (ret) {
+ dev_err(dev, "failed to sync pll regcache.\n");
+ goto stop_spba_clk;
+ }
}
- /* Release M0+ reset */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
- FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
- if (ret < 0) {
- dev_err(dev, "M0+ core release failed: %d\n", ret);
- goto stop_spba_clk;
- }
+ if (xcvr->mode == FSL_XCVR_MODE_EARC) {
+ ret = fsl_xcvr_load_firmware(xcvr);
+ if (ret) {
+ dev_err(dev, "failed to load firmware.\n");
+ goto stop_spba_clk;
+ }
+
+ /* Release M0+ reset */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
+ if (ret < 0) {
+ dev_err(dev, "M0+ core release failed: %d\n", ret);
+ goto stop_spba_clk;
+ }
- /* Let M0+ core complete firmware initialization */
- msleep(50);
+ /* Let M0+ core complete firmware initialization */
+ msleep(50);
+ }
return 0;
@@ -1483,21 +1820,18 @@ stop_ipg_clk:
}
static const struct dev_pm_ops fsl_xcvr_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend,
- fsl_xcvr_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver fsl_xcvr_driver = {
.probe = fsl_xcvr_probe,
.driver = {
- .name = "fsl,imx8mp-audio-xcvr",
- .pm = &fsl_xcvr_pm_ops,
+ .name = "fsl-xcvr",
+ .pm = pm_ptr(&fsl_xcvr_pm_ops),
.of_match_table = fsl_xcvr_dt_ids,
},
- .remove_new = fsl_xcvr_remove,
+ .remove = fsl_xcvr_remove,
};
module_platform_driver(fsl_xcvr_driver);
diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h
index 044058fc6aa2..dade3945cc0c 100644
--- a/sound/soc/fsl/fsl_xcvr.h
+++ b/sound/soc/fsl/fsl_xcvr.h
@@ -165,6 +165,11 @@
FSL_XCVR_IRQ_MUTE | \
FSL_XCVR_IRQ_FIFO_UOFL_ERR | \
FSL_XCVR_IRQ_HOST_WAKEUP | \
+ FSL_XCVR_IRQ_CMDC_STATUS_UPD |\
+ FSL_XCVR_IRQ_B_PRE_MISMATCH |\
+ FSL_XCVR_IRQ_M_W_PRE_MISMATCH |\
+ FSL_XCVR_IRQ_PREAMBLE_MISMATCH |\
+ FSL_XCVR_IRQ_UNEXP_PRE_REC |\
FSL_XCVR_IRQ_ARC_MODE)
#define FSL_XCVR_ISR_CMDC_TX_EN BIT(3)
@@ -229,6 +234,7 @@
#define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30)
#define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15)
+#define FSL_XCVR_PHY_AI_CTRL_AI_RWB BIT(31)
#define FSL_XCVR_PLL_CTRL0 0x00
#define FSL_XCVR_PLL_CTRL0_SET 0x04
@@ -236,13 +242,25 @@
#define FSL_XCVR_PLL_NUM 0x20
#define FSL_XCVR_PLL_DEN 0x30
#define FSL_XCVR_PLL_PDIV 0x40
+#define FSL_XCVR_PLL_BANDGAP 0x50
#define FSL_XCVR_PLL_BANDGAP_SET 0x54
+#define FSL_XCVR_PLL_STAT0 0x60
+#define FSL_XCVR_PLL_STAT0_TOG 0x6c
+
#define FSL_XCVR_PHY_CTRL 0x00
#define FSL_XCVR_PHY_CTRL_SET 0x04
#define FSL_XCVR_PHY_CTRL_CLR 0x08
+#define FSL_XCVR_PHY_CTRL_TOG 0x0c
+#define FSL_XCVR_PHY_STATUS 0x10
+#define FSL_XCVR_PHY_ANALOG_TRIM 0x20
+#define FSL_XCVR_PHY_SLEW_RATE_TRIM 0x30
+#define FSL_XCVR_PHY_DATA_TEST_DELAY 0x40
+#define FSL_XCVR_PHY_TEST_CTRL 0x50
+#define FSL_XCVR_PHY_DIFF_CDR_CTRL 0x60
#define FSL_XCVR_PHY_CTRL2 0x70
#define FSL_XCVR_PHY_CTRL2_SET 0x74
#define FSL_XCVR_PHY_CTRL2_CLR 0x78
+#define FSL_XCVR_PHY_CTRL2_TOG 0x7c
#define FSL_XCVR_PLL_BANDGAP_EN_VBG BIT(0)
#define FSL_XCVR_PLL_CTRL0_HROFF BIT(13)
@@ -291,4 +309,95 @@
#define FSL_XCVR_RX_CS_BUFF_1 0xA0 /* Second RX CS buffer */
#define FSL_XCVR_CAP_DATA_STR 0x300 /* Capabilities data structure */
+/* GP PLL Registers */
+#define FSL_XCVR_GP_PLL_CTRL 0x00
+#define FSL_XCVR_GP_PLL_CTRL_SET 0x04
+#define FSL_XCVR_GP_PLL_CTRL_CLR 0x08
+#define FSL_XCVR_GP_PLL_CTRL_TOG 0x0C
+#define FSL_XCVR_GP_PLL_ANA_PRG 0x10
+#define FSL_XCVR_GP_PLL_ANA_PRG_SET 0x14
+#define FSL_XCVR_GP_PLL_ANA_PRG_CLR 0x18
+#define FSL_XCVR_GP_PLL_ANA_PRG_TOG 0x1C
+#define FSL_XCVR_GP_PLL_TEST 0x20
+#define FSL_XCVR_GP_PLL_TEST_SET 0x24
+#define FSL_XCVR_GP_PLL_TEST_CLR 0x28
+#define FSL_XCVR_GP_PLL_TEST_TOG 0x2C
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM 0x30
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_SET 0x34
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_CLR 0x38
+#define FSL_XCVR_GP_PLL_SPREAD_SPECTRUM_TOG 0x3C
+#define FSL_XCVR_GP_PLL_NUMERATOR 0x40
+#define FSL_XCVR_GP_PLL_NUMERATOR_SET 0x44
+#define FSL_XCVR_GP_PLL_NUMERATOR_CLR 0x48
+#define FSL_XCVR_GP_PLL_NUMERATOR_TOG 0x4C
+#define FSL_XCVR_GP_PLL_DENOMINATOR 0x50
+#define FSL_XCVR_GP_PLL_DENOMINATOR_SET 0x54
+#define FSL_XCVR_GP_PLL_DENOMINATOR_CLR 0x58
+#define FSL_XCVR_GP_PLL_DENOMINATOR_TOG 0x5C
+#define FSL_XCVR_GP_PLL_DIV 0x60
+#define FSL_XCVR_GP_PLL_DIV_SET 0x64
+#define FSL_XCVR_GP_PLL_DIV_CLR 0x68
+#define FSL_XCVR_GP_PLL_DIV_TOG 0x6C
+#define FSL_XCVR_GP_PLL_DFS_CTRL0 0x70
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_SET 0x74
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_CLR 0x78
+#define FSL_XCVR_GP_PLL_DFS_CTRL0_TOG 0x7C
+#define FSL_XCVR_GP_PLL_DFS_DIV0 0x80
+#define FSL_XCVR_GP_PLL_DFS_DIV0_SET 0x84
+#define FSL_XCVR_GP_PLL_DFS_DIV0_CLR 0x88
+#define FSL_XCVR_GP_PLL_DFS_DIV0_TOG 0x8C
+#define FSL_XCVR_GP_PLL_DFS_CTRL1 0x90
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_SET 0x94
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_CLR 0x98
+#define FSL_XCVR_GP_PLL_DFS_CTRL1_TOG 0x9C
+#define FSL_XCVR_GP_PLL_DFS_DIV1 0xA0
+#define FSL_XCVR_GP_PLL_DFS_DIV1_SET 0xA4
+#define FSL_XCVR_GP_PLL_DFS_DIV1_CLR 0xA8
+#define FSL_XCVR_GP_PLL_DFS_DIV1_TOG 0xAC
+#define FSL_XCVR_GP_PLL_DFS_CTRL2 0xB0
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_SET 0xB4
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_CLR 0xB8
+#define FSL_XCVR_GP_PLL_DFS_CTRL2_TOG 0xBC
+#define FSL_XCVR_GP_PLL_DFS_DIV2 0xC0
+#define FSL_XCVR_GP_PLL_DFS_DIV2_SET 0xC4
+#define FSL_XCVR_GP_PLL_DFS_DIV2_CLR 0xC8
+#define FSL_XCVR_GP_PLL_DFS_DIV2_TOG 0xCC
+#define FSL_XCVR_GP_PLL_DFS_CTRL3 0xD0
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_SET 0xD4
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_CLR 0xD8
+#define FSL_XCVR_GP_PLL_DFS_CTRL3_TOG 0xDC
+#define FSL_XCVR_GP_PLL_DFS_DIV3 0xE0
+#define FSL_XCVR_GP_PLL_DFS_DIV3_SET 0xE4
+#define FSL_XCVR_GP_PLL_DFS_DIV3_CLR 0xE8
+#define FSL_XCVR_GP_PLL_DFS_DIV3_TOG 0xEC
+#define FSL_XCVR_GP_PLL_STATUS 0xF0
+#define FSL_XCVR_GP_PLL_STATUS_SET 0xF4
+#define FSL_XCVR_GP_PLL_STATUS_CLR 0xF8
+#define FSL_XCVR_GP_PLL_STATUS_TOG 0xFC
+
+/* GP PLL Control Register */
+#define FSL_XCVR_GP_PLL_CTRL_LBYPASS BIT(31)
+#define FSL_XCVR_GP_PLL_CTRL_HCS BIT(16)
+#define FSL_XCVR_GP_PLL_CTRL_MSD BIT(12)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN3 BIT(11)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN2 BIT(10)
+#define FSL_XCVR_GP_PLL_CTRL_DITHER_EN1 BIT(9)
+#define FSL_XCVR_GP_PLL_CTRL_SPREADCTL BIT(8)
+#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_BYPASS BIT(2)
+#define FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN BIT(1)
+#define FSL_XCVR_GP_PLL_CTRL_POWERUP BIT(0)
+
+/* GP PLL Numerator Register */
+#define FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT 2
+#define FSL_XCVR_GP_PLL_NUMERATOR_MFN GENMASK(31, 2)
+
+/* GP PLL Denominator Register */
+#define FSL_XCVR_GP_PLL_DENOMINATOR_MFD GENMASK(29, 0)
+
+/* GP PLL Dividers Register */
+#define FSL_XCVR_GP_PLL_DIV_MFI_SHIFT 16
+#define FSL_XCVR_GP_PLL_DIV_MFI GENMASK(24, 16)
+#define FSL_XCVR_GP_PLL_DIV_RDIV GENMASK(15, 13)
+#define FSL_XCVR_GP_PLL_DIV_ODIV GENMASK(7, 0)
+
#endif /* __FSL_XCVR_H */
diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c
index 289e47c03d40..38aafb8954c7 100644
--- a/sound/soc/fsl/imx-audio-rpmsg.c
+++ b/sound/soc/fsl/imx-audio-rpmsg.c
@@ -12,6 +12,7 @@
*/
struct imx_audio_rpmsg {
struct platform_device *rpmsg_pdev;
+ struct platform_device *card_pdev;
};
static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
@@ -87,14 +88,24 @@ static int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev)
/* Register platform driver for rpmsg routine */
data->rpmsg_pdev = platform_device_register_data(&rpdev->dev,
- IMX_PCM_DRV_NAME,
- PLATFORM_DEVID_AUTO,
+ rpdev->id.name,
+ PLATFORM_DEVID_NONE,
NULL, 0);
if (IS_ERR(data->rpmsg_pdev)) {
dev_err(&rpdev->dev, "failed to register rpmsg platform.\n");
ret = PTR_ERR(data->rpmsg_pdev);
}
+ data->card_pdev = platform_device_register_data(&rpdev->dev,
+ "imx-audio-rpmsg",
+ PLATFORM_DEVID_AUTO,
+ rpdev->id.name,
+ strlen(rpdev->id.name) + 1);
+ if (IS_ERR(data->card_pdev)) {
+ dev_err(&rpdev->dev, "failed to register rpmsg card.\n");
+ ret = PTR_ERR(data->card_pdev);
+ }
+
return ret;
}
@@ -105,6 +116,9 @@ static void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev)
if (data->rpmsg_pdev)
platform_device_unregister(data->rpmsg_pdev);
+ if (data->card_pdev)
+ platform_device_unregister(data->card_pdev);
+
dev_info(&rpdev->dev, "audio rpmsg driver is removed\n");
}
@@ -113,6 +127,7 @@ static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = {
{ .name = "rpmsg-micfil-channel" },
{ },
};
+MODULE_DEVICE_TABLE(rpmsg, imx_audio_rpmsg_id_table);
static struct rpmsg_driver imx_audio_rpmsg_driver = {
.drv.name = "imx_audio_rpmsg",
@@ -126,5 +141,5 @@ module_rpmsg_driver(imx_audio_rpmsg_driver);
MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface");
MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
-MODULE_ALIAS("platform:imx_audio_rpmsg");
+MODULE_ALIAS("rpmsg:imx_audio_rpmsg");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
index 2aeb18397bcb..dac5d4ddacd6 100644
--- a/sound/soc/fsl/imx-audmix.c
+++ b/sound/soc/fsl/imx-audmix.c
@@ -23,7 +23,6 @@ struct imx_audmix {
struct snd_soc_card card;
struct platform_device *audmix_pdev;
struct platform_device *out_pdev;
- struct clk *cpu_mclk;
int num_dai;
struct snd_soc_dai_link *dai;
int num_dai_conf;
@@ -32,34 +31,11 @@ struct imx_audmix {
struct snd_soc_dapm_route *dapm_routes;
};
-static const u32 imx_audmix_rates[] = {
- 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
-};
-
-static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints = {
- .count = ARRAY_SIZE(imx_audmix_rates),
- .list = imx_audmix_rates,
-};
-
static int imx_audmix_fe_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct imx_audmix *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct device *dev = rtd->card->dev;
- unsigned long clk_rate = clk_get_rate(priv->cpu_mclk);
int ret;
- if (clk_rate % 24576000 == 0) {
- ret = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &imx_audmix_rate_constraints);
- if (ret < 0)
- return ret;
- } else {
- dev_warn(dev, "mclk may be not supported %lu\n", clk_rate);
- }
-
ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
1, 8);
if (ret < 0)
@@ -140,6 +116,13 @@ static const struct snd_soc_ops imx_audmix_be_ops = {
.hw_params = imx_audmix_be_hw_params,
};
+static const char *name[][3] = {
+ {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"},
+ {"sai-tx", "sai-tx", "sai-rx"},
+ {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "SAI-Capture"},
+ {"SAI-Playback", "SAI-Playback", "AUDMIX-Capture-0"},
+};
+
static int imx_audmix_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -150,7 +133,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
struct imx_audmix *priv;
int i, num_dai, ret;
const char *fe_name_pref = "HiFi-AUDMIX-FE-";
- char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name;
+ char *be_name, *dai_name;
if (pdev->dev.parent) {
audmix_np = pdev->dev.parent->of_node;
@@ -183,6 +166,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
+ num_dai += 1;
priv->num_dai = 2 * num_dai;
priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai,
sizeof(struct snd_soc_dai_link), GFP_KERNEL);
@@ -196,7 +180,7 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!priv->dai_conf)
return -ENOMEM;
- priv->num_dapm_routes = 3 * num_dai;
+ priv->num_dapm_routes = num_dai;
priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes,
sizeof(struct snd_soc_dapm_route),
GFP_KERNEL);
@@ -211,8 +195,12 @@ static int imx_audmix_probe(struct platform_device *pdev)
if (!dlc)
return -ENOMEM;
- ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
- &args);
+ if (i == num_dai - 1)
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, 0,
+ &args);
+ else
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
+ &args);
if (ret < 0) {
dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n");
return ret;
@@ -226,20 +214,14 @@ static int imx_audmix_probe(struct platform_device *pdev)
put_device(&cpu_pdev->dev);
dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
- fe_name_pref, args.np->full_name + 1);
+ fe_name_pref, args.np->full_name);
if (!dai_name)
return -ENOMEM;
dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
- if (i == 0) {
+ if (i == num_dai - 1)
out_cpu_np = args.np;
- capture_dai_name =
- devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
- dai_name, "CPU-Capture");
- if (!capture_dai_name)
- return -ENOMEM;
- }
/*
* CPU == Platform
@@ -252,25 +234,23 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dai[i].num_cpus = 1;
priv->dai[i].num_codecs = 1;
priv->dai[i].num_platforms = 1;
-
- priv->dai[i].name = dai_name;
+ priv->dai[i].name = name[0][i];
priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
priv->dai[i].cpus->of_node = args.np;
- priv->dai[i].cpus->dai_name = dev_name(&cpu_pdev->dev);
+ priv->dai[i].cpus->dai_name = name[1][i];
+
priv->dai[i].dynamic = 1;
- priv->dai[i].dpcm_playback = 1;
- priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
+ if (i == num_dai - 1)
+ priv->dai[i].capture_only = 1;
+ else
+ priv->dai[i].playback_only = 1;
priv->dai[i].ignore_pmdown_time = 1;
priv->dai[i].ops = &imx_audmix_fe_ops;
/* Add AUDMIX Backend */
be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"audmix-%d", i);
- be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL,
- "AUDMIX-Playback-%d", i);
- be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
- "AUDMIX-Capture-%d", i);
- if (!be_name || !be_pb || !be_cp)
+ if (!be_name)
return -ENOMEM;
priv->dai[num_dai + i].cpus = &dlc[1];
@@ -283,25 +263,33 @@ static int imx_audmix_probe(struct platform_device *pdev)
priv->dai[num_dai + i].cpus->of_node = audmix_np;
priv->dai[num_dai + i].cpus->dai_name = be_name;
priv->dai[num_dai + i].no_pcm = 1;
- priv->dai[num_dai + i].dpcm_playback = 1;
- priv->dai[num_dai + i].dpcm_capture = 1;
+ if (i == num_dai - 1)
+ priv->dai[num_dai + i].capture_only = 1;
+ else
+ priv->dai[num_dai + i].playback_only = 1;
priv->dai[num_dai + i].ignore_pmdown_time = 1;
priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
priv->dai_conf[i].dlc.of_node = args.np;
priv->dai_conf[i].name_prefix = dai_name;
- priv->dapm_routes[i].source =
- devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
- dai_name, "CPU-Playback");
- if (!priv->dapm_routes[i].source)
- return -ENOMEM;
+ if (i == num_dai - 1) {
+ priv->dapm_routes[i].sink =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, name[2][i]);
+ if (!priv->dapm_routes[i].sink)
+ return -ENOMEM;
- priv->dapm_routes[i].sink = be_pb;
- priv->dapm_routes[num_dai + i].source = be_pb;
- priv->dapm_routes[num_dai + i].sink = be_cp;
- priv->dapm_routes[2 * num_dai + i].source = be_cp;
- priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name;
+ priv->dapm_routes[i].source = name[3][i];
+ } else {
+ priv->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, name[3][i]);
+ if (!priv->dapm_routes[i].source)
+ return -ENOMEM;
+
+ priv->dapm_routes[i].sink = name[2][i];
+ }
}
cpu_pdev = of_find_device_by_node(out_cpu_np);
@@ -311,13 +299,6 @@ static int imx_audmix_probe(struct platform_device *pdev)
}
put_device(&cpu_pdev->dev);
- priv->cpu_mclk = devm_clk_get(&cpu_pdev->dev, "mclk1");
- if (IS_ERR(priv->cpu_mclk)) {
- ret = PTR_ERR(priv->cpu_mclk);
- dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret);
- return ret;
- }
-
priv->audmix_pdev = audmix_pdev;
priv->out_pdev = cpu_pdev;
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index 747ab2f1aae3..cc2918ee2cf5 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -237,7 +237,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
child);
continue;
}
- if (!of_property_read_bool(child, "fsl,port-config")) {
+ if (!of_property_present(child, "fsl,port-config")) {
dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n",
child);
continue;
@@ -320,7 +320,6 @@ static void imx_audmux_remove(struct platform_device *pdev)
audmux_debugfs_remove();
}
-#ifdef CONFIG_PM_SLEEP
static int imx_audmux_suspend(struct device *dev)
{
int i;
@@ -348,18 +347,17 @@ static int imx_audmux_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops imx_audmux_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume)
+ SYSTEM_SLEEP_PM_OPS(imx_audmux_suspend, imx_audmux_resume)
};
static struct platform_driver imx_audmux_driver = {
.probe = imx_audmux_probe,
- .remove_new = imx_audmux_remove,
+ .remove = imx_audmux_remove,
.driver = {
.name = DRIVER_NAME,
- .pm = &imx_audmux_pm,
+ .pm = pm_sleep_ptr(&imx_audmux_pm),
.of_match_table = imx_audmux_dt_ids,
}
};
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index cb8723965f2f..9e668ae68039 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -25,6 +25,7 @@ enum codec_type {
CODEC_AK4458,
CODEC_AK4497,
CODEC_AK5552,
+ CODEC_CS42888,
};
/*
@@ -185,6 +186,16 @@ static struct imx_akcodec_tdm_fs_mul ak5558_tdm_fs_mul[] = {
{ .min = 512, .max = 512, .mul = 1024 },
};
+static struct imx_akcodec_fs_mul cs42888_fs_mul[] = {
+ { .rmin = 8000, .rmax = 48000, .wmin = 256, .wmax = 1024, },
+ { .rmin = 64000, .rmax = 96000, .wmin = 128, .wmax = 512, },
+ { .rmin = 176400, .rmax = 192000, .wmin = 64, .wmax = 256, },
+};
+
+static struct imx_akcodec_tdm_fs_mul cs42888_tdm_fs_mul[] = {
+ { .min = 256, .max = 256, .mul = 256 },
+};
+
static const u32 akcodec_rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
96000, 176400, 192000, 352800, 384000, 705600, 768000,
@@ -210,6 +221,14 @@ static const u32 ak5558_tdm_channels[] = {
1, 2, 3, 4, 5, 6, 7, 8,
};
+static const u32 cs42888_channels[] = {
+ 1, 2, 4, 6, 8,
+};
+
+static const u32 cs42888_tdm_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+};
+
static bool format_is_dsd(struct snd_pcm_hw_params *params)
{
snd_pcm_format_t format = params_format(params);
@@ -241,6 +260,7 @@ static bool codec_is_akcodec(unsigned int type)
case CODEC_AK4497:
case CODEC_AK5558:
case CODEC_AK5552:
+ case CODEC_CS42888:
return true;
default:
break;
@@ -252,10 +272,10 @@ static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
int slots, int slot_width)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct imx_card_data *data = snd_soc_card_get_drvdata(rtd->card);
const struct imx_card_plat_data *plat_data = data->plat_data;
- struct dai_link_data *link_data = &data->link_data[rtd->num];
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
unsigned int width = slots * slot_width;
unsigned int rate = params_rate(params);
int i;
@@ -289,11 +309,11 @@ static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream,
static int imx_aif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_card *card = rtd->card;
struct imx_card_data *data = snd_soc_card_get_drvdata(card);
- struct dai_link_data *link_data = &data->link_data[rtd->num];
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
struct imx_card_plat_data *plat_data = data->plat_data;
struct device *dev = card->dev;
struct snd_soc_dai *codec_dai;
@@ -340,13 +360,15 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- ret = snd_soc_dai_set_tdm_slot(codec_dai,
- BIT(slots) - 1,
- BIT(slots) - 1,
- slots, slot_width);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n", i, ret);
- return ret;
+ if (format_is_tdm(link_data)) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai,
+ BIT(slots) - 1,
+ BIT(slots) - 1,
+ slots, slot_width);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n", i, ret);
+ return ret;
+ }
}
}
@@ -370,6 +392,11 @@ static int imx_aif_hw_params(struct snd_pcm_substream *substream,
dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n", mclk_freq, ret);
return ret;
}
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dev, "failed to set codec dai mclk rate (%lu): %d\n", mclk_freq, ret);
+ return ret;
+ }
return 0;
}
@@ -405,10 +432,10 @@ static int ak5558_hw_rule_rate(struct snd_pcm_hw_params *p, struct snd_pcm_hw_ru
static int imx_aif_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct imx_card_data *data = snd_soc_card_get_drvdata(card);
- struct dai_link_data *link_data = &data->link_data[rtd->num];
+ struct dai_link_data *link_data = &data->link_data[rtd->id];
static struct snd_pcm_hw_constraint_list constraint_rates;
static struct snd_pcm_hw_constraint_list constraint_channels;
int ret = 0;
@@ -491,6 +518,7 @@ static int imx_card_parse_of(struct imx_card_data *data)
struct snd_soc_dai_link *link;
struct dai_link_data *link_data;
struct of_phandle_args args;
+ bool playback_only, capture_only;
int ret, num_links;
u32 asrc_fmt = 0;
u32 width;
@@ -502,7 +530,7 @@ static int imx_card_parse_of(struct imx_card_data *data)
}
/* DAPM routes */
- if (of_property_read_bool(dev->of_node, "audio-routing")) {
+ if (of_property_present(dev->of_node, "audio-routing")) {
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret)
return ret;
@@ -516,7 +544,7 @@ static int imx_card_parse_of(struct imx_card_data *data)
if (!card->dai_link)
return -ENOMEM;
- data->link_data = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+ data->link_data = devm_kcalloc(dev, num_links, sizeof(*link_data), GFP_KERNEL);
if (!data->link_data)
return -ENOMEM;
@@ -604,6 +632,8 @@ static int imx_card_parse_of(struct imx_card_data *data)
plat_data->type = CODEC_AK5558;
else if (!strcmp(link->codecs->dai_name, "ak5552-aif"))
plat_data->type = CODEC_AK5552;
+ else if (!strcmp(link->codecs->dai_name, "cs42888"))
+ plat_data->type = CODEC_CS42888;
} else {
link->codecs = &snd_soc_dummy_dlc;
@@ -640,9 +670,12 @@ static int imx_card_parse_of(struct imx_card_data *data)
}
} else if (!strncmp(link->name, "HiFi-ASRC-BE", 12)) {
/* DPCM backend */
+ /*
+ * No need to have link->platforms. alloced dlc[1] will be just wasted,
+ * but it won't leak.
+ */
link->no_pcm = 1;
- link->platforms->of_node = NULL;
- link->platforms->name = "snd-soc-dummy";
+ link->platforms = NULL;
link->be_hw_params_fixup = be_hw_params_fixup;
link->ops = &imx_aif_ops_be;
@@ -650,8 +683,9 @@ static int imx_card_parse_of(struct imx_card_data *data)
link->ops = &imx_aif_ops;
}
- if (link->no_pcm || link->dynamic)
- snd_soc_dai_link_set_capabilities(link);
+ graph_util_parse_link_direction(np, &playback_only, &capture_only);
+ link->playback_only = playback_only;
+ link->capture_only = capture_only;
/* Get dai fmt */
ret = simple_util_parse_daifmt(dev, np, codec,
@@ -713,6 +747,7 @@ static int imx_card_probe(struct platform_device *pdev)
data->plat_data = plat_data;
data->card.dev = &pdev->dev;
+ data->card.owner = THIS_MODULE;
dev_set_drvdata(&pdev->dev, &data->card);
snd_soc_card_set_drvdata(&data->card, data);
@@ -740,6 +775,8 @@ static int imx_card_probe(struct platform_device *pdev)
data->dapm_routes[i].sink =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
i + 1, "Playback");
+ if (!data->dapm_routes[i].sink)
+ return -ENOMEM;
data->dapm_routes[i].source = "CPU-Playback";
}
}
@@ -757,12 +794,20 @@ static int imx_card_probe(struct platform_device *pdev)
data->dapm_routes[i].source =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%d %s",
i + 1, "Capture");
+ if (!data->dapm_routes[i].source)
+ return -ENOMEM;
data->dapm_routes[i].sink = "CPU-Capture";
}
}
data->dapm_routes[i].sink = "ASRC-Capture";
data->dapm_routes[i].source = "CPU-Capture";
break;
+ case CODEC_CS42888:
+ data->dapm_routes[0].sink = "Playback";
+ data->dapm_routes[0].source = "CPU-Playback";
+ data->dapm_routes[1].sink = "CPU-Capture";
+ data->dapm_routes[1].source = "Capture";
+ break;
default:
break;
}
@@ -802,6 +847,16 @@ static int imx_card_probe(struct platform_device *pdev)
plat_data->support_tdm_channels = ak5558_tdm_channels;
plat_data->num_tdm_channels = ARRAY_SIZE(ak5558_tdm_channels);
break;
+ case CODEC_CS42888:
+ plat_data->fs_mul = cs42888_fs_mul;
+ plat_data->num_fs_mul = ARRAY_SIZE(cs42888_fs_mul);
+ plat_data->tdm_fs_mul = cs42888_tdm_fs_mul;
+ plat_data->num_tdm_fs_mul = ARRAY_SIZE(cs42888_tdm_fs_mul);
+ plat_data->support_channels = cs42888_channels;
+ plat_data->num_channels = ARRAY_SIZE(cs42888_channels);
+ plat_data->support_tdm_channels = cs42888_tdm_channels;
+ plat_data->num_tdm_channels = ARRAY_SIZE(cs42888_tdm_channels);
+ break;
default:
break;
}
@@ -817,8 +872,8 @@ static int imx_card_probe(struct platform_device *pdev)
}
for_each_card_prelinks(&data->card, i, link) {
if (link->dynamic == 1 && link_be) {
- link->dpcm_playback = link_be->dpcm_playback;
- link->dpcm_capture = link_be->dpcm_capture;
+ link->playback_only = link_be->playback_only;
+ link->capture_only = link_be->capture_only;
}
}
}
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
index 6f0d031c1d5f..3ef92f6dfc6b 100644
--- a/sound/soc/fsl/imx-es8328.c
+++ b/sound/soc/fsl/imx-es8328.c
@@ -3,12 +3,11 @@
// Copyright 2012 Freescale Semiconductor, Inc.
// Copyright 2012 Linaro Ltd.
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/i2c.h>
-#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
@@ -23,12 +22,11 @@ struct imx_es8328_data {
struct snd_soc_card card;
char codec_dai_name[DAI_NAME_SIZE];
char platform_name[DAI_NAME_SIZE];
- int jack_gpio;
+ struct gpio_desc *jack_gpiod;
};
static struct snd_soc_jack_gpio headset_jack_gpios[] = {
{
- .gpio = -1,
.name = "headset-gpio",
.report = SND_JACK_HEADSET,
.invert = 0,
@@ -54,8 +52,8 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
struct imx_es8328_data, card);
int ret = 0;
- /* Headphone jack detection */
- if (gpio_is_valid(data->jack_gpio)) {
+ if (data->jack_gpiod) {
+ /* Headphone jack detection */
ret = snd_soc_card_jack_new_pins(rtd->card, "Headphone",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&headset_jack,
@@ -64,7 +62,7 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
- headset_jack_gpios[0].gpio = data->jack_gpio;
+ headset_jack_gpios[0].desc = data->jack_gpiod;
ret = snd_soc_jack_add_gpios(&headset_jack,
ARRAY_SIZE(headset_jack_gpios),
headset_jack_gpios);
@@ -174,7 +172,11 @@ static int imx_es8328_probe(struct platform_device *pdev)
data->dev = dev;
- data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
+ data->jack_gpiod = devm_gpiod_get_optional(dev, "jack", GPIOD_IN);
+ if (IS_ERR(data->jack_gpiod)) {
+ ret = PTR_ERR(data->jack_gpiod);
+ goto put_device;
+ }
/*
* CPU == Platform
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index e454085c6e5c..fe47b439a818 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -32,7 +32,7 @@ struct imx_hdmi_data {
static int imx_hdmi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct imx_hdmi_data *data = snd_soc_card_get_drvdata(rtd->card);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index 14e94270911c..4fa208d6a032 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -50,4 +50,5 @@ int imx_pcm_dma_init(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
+MODULE_DESCRIPTION("Freescale i.MX PCM DMA interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 0d124002678e..83de3ae33691 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -185,8 +185,7 @@ static int snd_imx_open(struct snd_soc_component *component,
atomic_set(&iprtd->playing, 0);
atomic_set(&iprtd->capturing, 0);
- hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- iprtd->hrt.function = snd_hrtimer_callback;
+ hrtimer_setup(&iprtd->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
@@ -319,4 +318,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);
+MODULE_DESCRIPTION("Freescale i.MX PCM FIQ handler");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index fb9244c1e9c5..8ed62d43ffd5 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -16,7 +16,7 @@
#include "fsl_rpmsg.h"
#include "imx-pcm-rpmsg.h"
-static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
+static const struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_BATCH |
@@ -261,7 +261,7 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
info->send_message(msg, info);
pcm_hardware = imx_rpmsg_pcm_hardware;
- pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
+ pcm_hardware.buffer_bytes_max = rpmsg->buffer_size[substream->stream];
pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
@@ -301,7 +301,7 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
info->send_message(msg, info);
- del_timer(&info->stream_timer[substream->stream].timer);
+ timer_delete(&info->stream_timer[substream->stream].timer);
rtd->dai_link->ignore_suspend = 0;
@@ -316,7 +316,7 @@ static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
@@ -452,7 +452,7 @@ static int imx_rpmsg_terminate_all(struct snd_soc_component *component,
info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
}
- del_timer(&info->stream_timer[substream->stream].timer);
+ timer_delete(&info->stream_timer[substream->stream].timer);
return imx_rpmsg_insert_workqueue(substream, msg, info);
}
@@ -461,7 +461,7 @@ static int imx_rpmsg_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
int ret = 0;
@@ -515,7 +515,7 @@ static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
struct rpmsg_info *info = dev_get_drvdata(component->dev);
@@ -597,14 +597,29 @@ static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
struct snd_pcm *pcm = rtd->pcm;
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct snd_pcm_substream *substream;
int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
- return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
- pcm->card->dev, rpmsg->buffer_size);
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK]);
+ if (ret < 0)
+ return ret;
+ }
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
}
static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
@@ -732,9 +747,6 @@ static int imx_rpmsg_pcm_probe(struct platform_device *pdev)
goto fail;
}
- /* platform component name is used by machine driver to link with */
- component->name = info->rpdev->id.name;
-
#ifdef CONFIG_DEBUG_FS
component->debugfs_prefix = "rpmsg";
#endif
@@ -756,7 +768,6 @@ static void imx_rpmsg_pcm_remove(struct platform_device *pdev)
destroy_workqueue(info->rpmsg_wq);
}
-#ifdef CONFIG_PM
static int imx_rpmsg_pcm_runtime_resume(struct device *dev)
{
struct rpmsg_info *info = dev_get_drvdata(dev);
@@ -774,9 +785,7 @@ static int imx_rpmsg_pcm_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int imx_rpmsg_pcm_suspend(struct device *dev)
{
struct rpmsg_info *info = dev_get_drvdata(dev);
@@ -812,22 +821,27 @@ static int imx_rpmsg_pcm_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
- SET_RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
- imx_rpmsg_pcm_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend,
- imx_rpmsg_pcm_resume)
+ RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
+ imx_rpmsg_pcm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend, imx_rpmsg_pcm_resume)
+};
+
+static const struct platform_device_id imx_rpmsg_pcm_id_table[] = {
+ { .name = "rpmsg-audio-channel" },
+ { .name = "rpmsg-micfil-channel" },
+ { },
};
+MODULE_DEVICE_TABLE(platform, imx_rpmsg_pcm_id_table);
static struct platform_driver imx_pcm_rpmsg_driver = {
.probe = imx_rpmsg_pcm_probe,
- .remove_new = imx_rpmsg_pcm_remove,
+ .remove = imx_rpmsg_pcm_remove,
+ .id_table = imx_rpmsg_pcm_id_table,
.driver = {
.name = IMX_PCM_DRV_NAME,
- .pm = &imx_rpmsg_pcm_pm_ops,
+ .pm = pm_ptr(&imx_rpmsg_pcm_pm_ops),
},
};
module_platform_driver(imx_pcm_rpmsg_driver);
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index e5bd63dab10c..7cd3aa4c8706 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -5,9 +5,7 @@
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/i2c.h>
-#include <linux/of_gpio.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/jack.h>
@@ -108,10 +106,8 @@ static int imx_rpmsg_late_probe(struct snd_soc_card *card)
static int imx_rpmsg_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link_component *dlc;
- struct device *dev = pdev->dev.parent;
- /* rpmsg_pdev is the platform device for the rpmsg node that probed us */
- struct platform_device *rpmsg_pdev = to_platform_device(dev);
- struct device_node *np = rpmsg_pdev->dev.of_node;
+ struct snd_soc_dai *cpu_dai;
+ struct device_node *np = NULL;
struct of_phandle_args args;
const char *platform_name;
struct imx_rpmsg *data;
@@ -127,10 +123,6 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
goto fail;
}
- ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);
- if (ret)
- dev_warn(&pdev->dev, "no reserved DMA memory\n");
-
data->dai.cpus = &dlc[0];
data->dai.num_cpus = 1;
data->dai.platforms = &dlc[1];
@@ -152,6 +144,23 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
*/
data->dai.ignore_pmdown_time = 1;
+ data->dai.cpus->dai_name = pdev->dev.platform_data;
+ cpu_dai = snd_soc_find_dai(data->dai.cpus);
+ if (!cpu_dai) {
+ ret = -EPROBE_DEFER;
+ goto fail;
+ }
+ np = cpu_dai->dev->of_node;
+ if (!np) {
+ dev_err(&pdev->dev, "failed to parse CPU DAI device node\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);
+ if (ret)
+ dev_warn(&pdev->dev, "no reserved DMA memory\n");
+
/* Optional codec node */
ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args);
if (ret) {
@@ -170,7 +179,6 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
data->sysclk = clk_get_rate(clk);
}
- data->dai.cpus->dai_name = dev_name(&rpmsg_pdev->dev);
if (!of_property_read_string(np, "fsl,rpmsg-channel-name", &platform_name))
data->dai.platforms->name = platform_name;
else
@@ -210,7 +218,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
if (ret)
goto fail;
- if (of_property_read_bool(np, "audio-routing")) {
+ if (of_property_present(np, "audio-routing")) {
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 3c1e69092d2f..8bcf54ef709e 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -214,7 +214,7 @@ static struct platform_driver imx_sgtl5000_driver = {
.of_match_table = imx_sgtl5000_dt_ids,
},
.probe = imx_sgtl5000_probe,
- .remove_new = imx_sgtl5000_remove,
+ .remove = imx_sgtl5000_remove,
};
module_platform_driver(imx_sgtl5000_driver);
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
deleted file mode 100644
index 1e57939a7e29..000000000000
--- a/sound/soc/fsl/imx-spdif.c
+++ /dev/null
@@ -1,103 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-//
-// Copyright (C) 2013 Freescale Semiconductor, Inc.
-
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <sound/soc.h>
-
-struct imx_spdif_data {
- struct snd_soc_dai_link dai;
- struct snd_soc_card card;
-};
-
-static int imx_spdif_audio_probe(struct platform_device *pdev)
-{
- struct device_node *spdif_np, *np = pdev->dev.of_node;
- struct imx_spdif_data *data;
- struct snd_soc_dai_link_component *comp;
- int ret = 0;
-
- spdif_np = of_parse_phandle(np, "spdif-controller", 0);
- if (!spdif_np) {
- dev_err(&pdev->dev, "failed to find spdif-controller\n");
- ret = -EINVAL;
- goto end;
- }
-
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL);
- if (!data || !comp) {
- ret = -ENOMEM;
- goto end;
- }
-
- /*
- * CPU == Platform
- * platform is using soc-generic-dmaengine-pcm
- */
- data->dai.cpus =
- data->dai.platforms = comp;
- data->dai.codecs = &snd_soc_dummy_dlc;
-
- data->dai.num_cpus = 1;
- data->dai.num_codecs = 1;
- data->dai.num_platforms = 1;
-
- data->dai.name = "S/PDIF PCM";
- data->dai.stream_name = "S/PDIF PCM";
- data->dai.cpus->of_node = spdif_np;
- data->dai.playback_only = true;
- data->dai.capture_only = true;
-
- if (of_property_read_bool(np, "spdif-out"))
- data->dai.capture_only = false;
-
- if (of_property_read_bool(np, "spdif-in"))
- data->dai.playback_only = false;
-
- if (data->dai.playback_only && data->dai.capture_only) {
- dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
- goto end;
- }
-
- data->card.dev = &pdev->dev;
- data->card.dai_link = &data->dai;
- data->card.num_links = 1;
- data->card.owner = THIS_MODULE;
-
- ret = snd_soc_of_parse_card_name(&data->card, "model");
- if (ret)
- goto end;
-
- ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
- if (ret)
- dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
-
-end:
- of_node_put(spdif_np);
-
- return ret;
-}
-
-static const struct of_device_id imx_spdif_dt_ids[] = {
- { .compatible = "fsl,imx-audio-spdif", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
-
-static struct platform_driver imx_spdif_driver = {
- .driver = {
- .name = "imx-spdif",
- .pm = &snd_soc_pm_ops,
- .of_match_table = imx_spdif_dt_ids,
- },
- .probe = imx_spdif_audio_probe,
-};
-
-module_platform_driver(imx_spdif_driver);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx-spdif");
diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c
new file mode 100644
index 000000000000..c65c17dfa174
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-i2s.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Author: Kevin Wells <kevin.wells@nxp.com>
+//
+// Copyright (C) 2008 NXP Semiconductors
+// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "lpc3xxx-i2s.h"
+
+#define I2S_PLAYBACK_FLAG 0x1
+#define I2S_CAPTURE_FLAG 0x2
+
+#define LPC3XXX_I2S_RATES ( \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define LPC3XXX_I2S_FORMATS ( \
+ SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate)
+{
+ u32 i2srate;
+ u32 idxx, idyy;
+ u32 diff, trate, baseclk;
+
+ /* Adjust rate for sample size (bits) and 2 channels and offset for
+ * divider in clock output
+ */
+ i2srate = (freq / 100) * 2 * (8 * xbytes);
+ i2srate = i2srate << 1;
+ clkrate = clkrate / 100;
+ baseclk = clkrate;
+ *clkx = 1;
+ *clky = 1;
+
+ /* Find the best divider */
+ *clkx = *clky = 0;
+ diff = ~0;
+ for (idxx = 1; idxx < 0xFF; idxx++) {
+ for (idyy = 1; idyy < 0xFF; idyy++) {
+ trate = (baseclk * idxx) / idyy;
+ if (abs(trate - i2srate) < diff) {
+ diff = abs(trate - i2srate);
+ *clkx = idxx;
+ *clky = idyy;
+ }
+ }
+ }
+}
+
+static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+ u32 flag;
+ int ret = 0;
+
+ guard(mutex)(&i2s_info_p->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ flag = I2S_PLAYBACK_FLAG;
+ else
+ flag = I2S_CAPTURE_FLAG;
+
+ if (flag & i2s_info_p->streams_in_use) {
+ dev_warn(dev, "I2S channel is busy\n");
+ ret = -EBUSY;
+ return ret;
+ }
+
+ if (i2s_info_p->streams_in_use == 0) {
+ ret = clk_prepare_enable(i2s_info_p->clk);
+ if (ret) {
+ dev_err(dev, "Can't enable clock, err=%d\n", ret);
+ return ret;
+ }
+ }
+
+ i2s_info_p->streams_in_use |= flag;
+ return 0;
+}
+
+static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regs = i2s_info_p->regs;
+ const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP);
+ u32 flag;
+
+ guard(mutex)(&i2s_info_p->lock);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ flag = I2S_PLAYBACK_FLAG;
+ regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0);
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits);
+ } else {
+ flag = I2S_CAPTURE_FLAG;
+ regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0);
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits);
+ }
+ i2s_info_p->streams_in_use &= ~flag;
+
+ if (i2s_info_p->streams_in_use == 0)
+ clk_disable_unprepare(i2s_info_p->clk);
+}
+
+static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* Will use in HW params later */
+ i2s_info_p->freq = freq;
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+
+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) {
+ dev_warn(dev, "unsupported bus format %d\n", fmt);
+ return -EINVAL;
+ }
+
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ dev_warn(dev, "unsupported clock provider %d\n", fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct device *dev = i2s_info_p->dev;
+ struct regmap *regs = i2s_info_p->regs;
+ int xfersize;
+ u32 tmp, clkx, clky;
+
+ tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP);
+ xfersize = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP);
+ xfersize = 2;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP);
+ xfersize = 4;
+ break;
+
+ default:
+ dev_warn(dev, "Unsupported audio data format %d\n", params_format(params));
+ return -EINVAL;
+ }
+
+ if (params_channels(params) == 1)
+ tmp |= LPC3XXX_I2S_MONO;
+
+ __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate);
+
+ dev_dbg(dev, "Stream : %s\n", snd_pcm_direction_name(substream->stream));
+ dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq);
+ dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate);
+ dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize);
+ dev_dbg(dev, "Clock divider (x) : %d\n", clkx);
+ dev_dbg(dev, "Clock divider (y) : %d\n", clky);
+ dev_dbg(dev, "Channels : %d\n", params_channels(params));
+ dev_dbg(dev, "Data format : %s\n", "I2S");
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(regs, LPC3XXX_REG_I2S_DMA1,
+ LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4));
+ regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky);
+ regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp);
+ } else {
+ regmap_write(regs, LPC3XXX_REG_I2S_DMA0,
+ LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4));
+ regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky);
+ regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp);
+ }
+
+ return 0;
+}
+
+static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regs = i2s_info_p->regs;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
+ LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
+ else
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
+ LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP);
+ break;
+
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO,
+ (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
+ else
+ regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI,
+ (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config,
+ &i2s_info_p->capture_dma_config);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = {
+ .probe = lpc3xxx_i2s_dai_probe,
+ .startup = lpc3xxx_i2s_startup,
+ .shutdown = lpc3xxx_i2s_shutdown,
+ .trigger = lpc3xxx_i2s_trigger,
+ .hw_params = lpc3xxx_i2s_hw_params,
+ .set_sysclk = lpc3xxx_i2s_set_dai_sysclk,
+ .set_fmt = lpc3xxx_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = LPC3XXX_I2S_RATES,
+ .formats = LPC3XXX_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = LPC3XXX_I2S_RATES,
+ .formats = LPC3XXX_I2S_FORMATS,
+ },
+ .ops = &lpc3xxx_i2s_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const struct snd_soc_component_driver lpc32xx_i2s_component = {
+ .name = "lpc32xx-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static const struct regmap_config lpc32xx_i2s_regconfig = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = LPC3XXX_REG_I2S_RX_RATE,
+};
+
+static int lpc32xx_i2s_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lpc3xxx_i2s_info *i2s_info_p;
+ struct resource *res;
+ void __iomem *iomem;
+ int ret;
+
+ i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL);
+ if (!i2s_info_p)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2s_info_p);
+ i2s_info_p->dev = dev;
+
+ iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(iomem))
+ return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n");
+
+ i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig);
+ if (IS_ERR(i2s_info_p->regs))
+ return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs),
+ "failed to init register map: %pe\n", i2s_info_p->regs);
+
+ i2s_info_p->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(i2s_info_p->clk))
+ return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n");
+
+ i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk);
+ if (i2s_info_p->clkrate == 0)
+ return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n");
+
+ mutex_init(&i2s_info_p->lock);
+
+ ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component,
+ &lpc3xxx_i2s_dai_driver, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't register cpu_dai component\n");
+
+ i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO);
+ i2s_info_p->playback_dma_config.maxburst = 4;
+
+ i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO);
+ i2s_info_p->capture_dma_config.maxburst = 4;
+
+ ret = lpc3xxx_pcm_register(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't register pcm component\n");
+
+ return 0;
+}
+
+static const struct of_device_id lpc32xx_i2s_match[] = {
+ { .compatible = "nxp,lpc3220-i2s" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match);
+
+static struct platform_driver lpc32xx_i2s_driver = {
+ .probe = lpc32xx_i2s_probe,
+ .driver = {
+ .name = "lpc3xxx-i2s",
+ .of_match_table = lpc32xx_i2s_match,
+ },
+};
+
+module_platform_driver(lpc32xx_i2s_driver);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_AUTHOR("Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>");
+MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h
new file mode 100644
index 000000000000..b6657853017a
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-i2s.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2008 NXP Semiconductors
+ * Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+ */
+
+#ifndef __SOUND_SOC_LPC3XXX_I2S_H
+#define __SOUND_SOC_LPC3XXX_I2S_H
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+#include <linux/regmap.h>
+
+struct lpc3xxx_i2s_info {
+ struct device *dev;
+ struct clk *clk;
+ struct mutex lock; /* To serialize user-space access */
+ struct regmap *regs;
+ u32 streams_in_use;
+ u32 clkrate;
+ int freq;
+ struct snd_dmaengine_dai_dma_data playback_dma_config;
+ struct snd_dmaengine_dai_dma_data capture_dma_config;
+};
+
+int lpc3xxx_pcm_register(struct platform_device *pdev);
+
+/* I2S controller register offsets */
+#define LPC3XXX_REG_I2S_DAO 0x00
+#define LPC3XXX_REG_I2S_DAI 0x04
+#define LPC3XXX_REG_I2S_TX_FIFO 0x08
+#define LPC3XXX_REG_I2S_RX_FIFO 0x0C
+#define LPC3XXX_REG_I2S_STAT 0x10
+#define LPC3XXX_REG_I2S_DMA0 0x14
+#define LPC3XXX_REG_I2S_DMA1 0x18
+#define LPC3XXX_REG_I2S_IRQ 0x1C
+#define LPC3XXX_REG_I2S_TX_RATE 0x20
+#define LPC3XXX_REG_I2S_RX_RATE 0x24
+
+/* i2s_daO i2s_dai register definitions */
+#define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */
+#define LPC3XXX_I2S_WW16 FIELD_PREP(0x3, 1) /* Word width is 16bit */
+#define LPC3XXX_I2S_WW32 FIELD_PREP(0x3, 3) /* Word width is 32bit */
+#define LPC3XXX_I2S_MONO BIT(2) /* Mono */
+#define LPC3XXX_I2S_STOP BIT(3) /* Stop, diables the access to FIFO, mutes the channel */
+#define LPC3XXX_I2S_RESET BIT(4) /* Reset the channel */
+#define LPC3XXX_I2S_WS_SEL BIT(5) /* Channel Master(0) or slave(1) mode select */
+#define LPC3XXX_I2S_WS_HP(s) FIELD_PREP(0x7FC0, s) /* Word select half period - 1 */
+#define LPC3XXX_I2S_MUTE BIT(15) /* Mute the channel, Transmit channel only */
+
+#define LPC3XXX_I2S_WW32_HP 0x1f /* Word select half period for 32bit word width */
+#define LPC3XXX_I2S_WW16_HP 0x0f /* Word select half period for 16bit word width */
+#define LPC3XXX_I2S_WW8_HP 0x7 /* Word select half period for 8bit word width */
+
+/* i2s_stat register definitions */
+#define LPC3XXX_I2S_IRQ_STAT BIT(0)
+#define LPC3XXX_I2S_DMA0_REQ BIT(1)
+#define LPC3XXX_I2S_DMA1_REQ BIT(2)
+
+/* i2s_dma0 Configuration register definitions */
+#define LPC3XXX_I2S_DMA0_RX_EN BIT(0) /* Enable RX DMA1 */
+#define LPC3XXX_I2S_DMA0_TX_EN BIT(1) /* Enable TX DMA1 */
+#define LPC3XXX_I2S_DMA0_RX_DEPTH(s) FIELD_PREP(0xF00, s) /* Set the DMA1 RX Request level */
+#define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */
+
+/* i2s_dma1 Configuration register definitions */
+#define LPC3XXX_I2S_DMA1_RX_EN BIT(0) /* Enable RX DMA1 */
+#define LPC3XXX_I2S_DMA1_TX_EN BIT(1) /* Enable TX DMA1 */
+#define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */
+#define LPC3XXX_I2S_DMA1_TX_DEPTH(s) FIELD_PREP(0x70000, s) /* Set the DMA1 TX Request level */
+
+/* i2s_irq register definitions */
+#define LPC3XXX_I2S_RX_IRQ_EN BIT(0) /* Enable RX IRQ */
+#define LPC3XXX_I2S_TX_IRQ_EN BIT(1) /* Enable TX IRQ */
+#define LPC3XXX_I2S_IRQ_RX_DEPTH(s) FIELD_PREP(0xFF00, s) /* valid values ar 0 to 7 */
+#define LPC3XXX_I2S_IRQ_TX_DEPTH(s) FIELD_PREP(0xFF0000, s) /* valid values ar 0 to 7 */
+
+#endif
diff --git a/sound/soc/fsl/lpc3xxx-pcm.c b/sound/soc/fsl/lpc3xxx-pcm.c
new file mode 100644
index 000000000000..e6abaf63895a
--- /dev/null
+++ b/sound/soc/fsl/lpc3xxx-pcm.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Author: Kevin Wells <kevin.wells@nxp.com>
+//
+// Copyright (C) 2008 NXP Semiconductors
+// Copyright 2023 Timesys Corporation <piotr.wojtaszczyk@timesys.com>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/pl08x.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+#include "lpc3xxx-i2s.h"
+
+#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE | \
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static const struct snd_pcm_hardware lpc3xxx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = STUB_FORMATS,
+ .period_bytes_min = 128,
+ .period_bytes_max = 2048,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 128 * 1024
+};
+
+static const struct snd_dmaengine_pcm_config lpc3xxx_dmaengine_pcm_config = {
+ .pcm_hardware = &lpc3xxx_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = pl08x_filter_id,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+static const struct snd_soc_component_driver lpc3xxx_soc_platform_driver = {
+ .name = "lpc32xx-pcm",
+};
+
+int lpc3xxx_pcm_register(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &lpc3xxx_dmaengine_pcm_config, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dmaengine: %d\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&pdev->dev, &lpc3xxx_soc_platform_driver,
+ NULL, 0);
+}
+EXPORT_SYMBOL(lpc3xxx_pcm_register);
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 0423cf43c7a0..8554fb690772 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -327,7 +327,7 @@ MODULE_DEVICE_TABLE(of, psc_ac97_match);
static struct platform_driver psc_ac97_driver = {
.probe = psc_ac97_of_probe,
- .remove_new = psc_ac97_of_remove,
+ .remove = psc_ac97_of_remove,
.driver = {
.name = "mpc5200-psc-ac97",
.of_match_table = psc_ac97_match,
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index af8b9d098d2d..9ad44eeed6ad 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -184,7 +184,7 @@ static int psc_i2s_of_probe(struct platform_device *op)
/* Check for the codec handle. If it is not present then we
* are done */
- if (!of_get_property(op->dev.of_node, "codec-handle", NULL))
+ if (!of_property_present(op->dev.of_node, "codec-handle"))
return 0;
/* Due to errata in the dma mode; need to line up enabling
@@ -225,7 +225,7 @@ MODULE_DEVICE_TABLE(of, psc_i2s_match);
static struct platform_driver psc_i2s_driver = {
.probe = psc_i2s_of_probe,
- .remove_new = psc_i2s_of_remove,
+ .remove = psc_i2s_of_remove,
.driver = {
.name = "mpc5200-psc-i2s",
.of_match_table = psc_i2s_match,
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 6f5eecf6d88c..66db05970d82 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -408,7 +408,7 @@ static void p1022_ds_remove(struct platform_device *pdev)
static struct platform_driver p1022_ds_driver = {
.probe = p1022_ds_probe,
- .remove_new = p1022_ds_remove,
+ .remove = p1022_ds_remove,
.driver = {
/*
* The name must match 'compatible' property in the device tree,
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
index a42149311c8b..d4568346714f 100644
--- a/sound/soc/fsl/p1022_rdk.c
+++ b/sound/soc/fsl/p1022_rdk.c
@@ -370,7 +370,7 @@ static void p1022_rdk_remove(struct platform_device *pdev)
static struct platform_driver p1022_rdk_driver = {
.probe = p1022_rdk_probe,
- .remove_new = p1022_rdk_remove,
+ .remove = p1022_rdk_remove,
.driver = {
/*
* The name must match 'compatible' property in the device tree,
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
index 2bab0fc1de59..5542c4ee6d12 100644
--- a/sound/soc/fsl/pcm030-audio-fabric.c
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -124,7 +124,7 @@ MODULE_DEVICE_TABLE(of, pcm030_audio_match);
static struct platform_driver pcm030_fabric_driver = {
.probe = pcm030_fabric_probe,
- .remove_new = pcm030_fabric_remove,
+ .remove = pcm030_fabric_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = pcm030_audio_match,