From: Viorel Suman viorel.suman@nxp.com
Implement SPDIF bypass mode. It implies internal SoC routing of SPDIF input signal to SPDIF output signal. The test bed requires two boards: B1 configured in bypass mode, and B2 to feed B1 SPDIF RX port and read B1 SPDIF TX port: B2 TX -> B1 RX, B2 RX <- B1 TX. The test procedure: a) Boot both boards b) B2: start "arecord <spdifcard> -r 48kHz | aplay <local DAC>" c) B2: start "aplay <spdifcard> -r 48kHz <2ch 48kHz audio file>" d) B1: enable bypass mode: amixer -cimxspdif cset numid=8,iface=PCM,name='Bypass Mode' on e) B2: check DAC audio, make sure the same sample rate is used at steps b) and c), in example above the rate is 48kHz. f) B1: try to run "aplay" or "arecord" on imxspdif card while in bypass mode - both must fail until bypass mode is disabled g) B1: disable bypass mode: amixer -cimxspdif cset numid=8,iface=PCM,name='Bypass Mode' off h) B1: check the usual playback and capture on imxspdif card. During this test try to set bypass mode - must not be allowed while playback or capture is running.
Signed-off-by: Viorel Suman viorel.suman@nxp.com Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/fsl_spdif.c | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 928b59069283..d178b479c8bd 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -111,6 +111,7 @@ struct spdif_mixer_control { * @dma_params_tx: DMA parameters for transmit channel * @dma_params_rx: DMA parameters for receive channel * @regcache_srpc: regcache for SRPC + * @bypass: status of bypass input to output */ struct fsl_spdif_priv { const struct fsl_spdif_soc_data *soc; @@ -133,6 +134,7 @@ struct fsl_spdif_priv { struct snd_dmaengine_dai_dma_data dma_params_rx; /* regcache for SRPC */ u32 regcache_srpc; + bool bypass; };
static struct fsl_spdif_soc_data fsl_spdif_vf610 = { @@ -905,6 +907,69 @@ static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol, return 0; }
+static int fsl_spdif_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.integer.value[0] = priv->bypass ? 1 : 0; + + return 0; +} + +static int fsl_spdif_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_card *card = dai->component->card; + bool set = (ucontrol->value.integer.value[0] != 0); + struct regmap *regmap = priv->regmap; + struct snd_soc_pcm_runtime *rtd; + u32 scr, mask; + int stream; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link); + + if (priv->bypass == set) + return 0; /* nothing to do */ + + if (snd_soc_dai_active(dai)) { + dev_err(dai->dev, "Cannot change BYPASS mode while stream is running.\n"); + return -EBUSY; + } + + pm_runtime_get_sync(dai->dev); + + if (set) { + /* Disable interrupts */ + regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0); + + /* Configure BYPASS mode */ + scr = SCR_TXSEL_RX | SCR_RXFIFO_OFF; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK | + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK | SCR_TXSEL_MASK; + /* Power up SPDIF module */ + mask |= SCR_LOW_POWER; + } else { + /* Power down SPDIF module, disable TX */ + scr = SCR_LOW_POWER | SCR_TXSEL_OFF; + mask = SCR_LOW_POWER | SCR_TXSEL_MASK; + } + + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Disable playback & capture if BYPASS mode is enabled, enable otherwise */ + for_each_pcm_streams(stream) + rtd->pcm->streams[stream].substream_count = (set ? 0 : 1); + + priv->bypass = set; + pm_runtime_put_sync(dai->dev); + + return 0; +} + /* DPLL lock information */ static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1075,6 +1140,15 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = { .info = fsl_spdif_rxrate_info, .get = fsl_spdif_rxrate_get, }, + /* RX bypass controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Bypass Mode", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = fsl_spdif_bypass_get, + .put = fsl_spdif_bypass_put, + }, /* User bit sync mode set/get controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM,