@@ -59,6 +59,7 @@ config SND_SOC_FSL_SPDIF
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC)
select BITREVERSE
+ select SND_SOC_FSL_UTILS
help
Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
support for the Freescale CPUs.
@@ -23,6 +23,7 @@
#include <sound/soc.h>
#include "fsl_spdif.h"
+#include "fsl_utils.h"
#include "imx-pcm.h"
#define FSL_SPDIF_TXFIFO_WML 0x8
@@ -114,6 +115,8 @@ struct spdif_mixer_control {
* @dma_params_rx: DMA parameters for receive channel
* @regcache_srpc: regcache for SRPC
* @bypass: status of bypass input to output
+ * @pll8k_clk: PLL clock for the rate of multiply of 8kHz
+ * @pll11k_clk: PLL clock for the rate of multiply of 11kHz
*/
struct fsl_spdif_priv {
const struct fsl_spdif_soc_data *soc;
@@ -137,6 +140,8 @@ struct fsl_spdif_priv {
/* regcache for SRPC */
u32 regcache_srpc;
bool bypass;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
};
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
@@ -480,6 +485,8 @@ static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
return 0;
}
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index);
+
static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
int sample_rate)
{
@@ -528,6 +535,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ ret = fsl_spdif_probe_txclk(spdif_priv, rate);
+ if (ret)
+ return ret;
+
clk = spdif_priv->txclk_src[rate];
if (clk >= STC_TXCLK_SRC_MAX) {
dev_err(&pdev->dev, "tx clock source is out of range\n");
@@ -647,6 +658,38 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
}
}
+static int spdif_reparent_rootclk(struct fsl_spdif_priv *spdif_priv, unsigned int sample_rate)
+{
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct device *dev = &pdev->dev;
+ u64 ratio = sample_rate;
+ struct clk *clk;
+ int ret;
+
+ /* Reparent clock if required condition is true */
+ if (!spdif_priv->pll8k_clk || !spdif_priv->pll11k_clk ||
+ !fsl_spdif_can_set_clk_rate(spdif_priv, STC_TXCLK_SPDIF_ROOT))
+ return 0;
+
+ ratio = do_div(ratio, 8000) ? CLK_11K_FREQ : CLK_8K_FREQ;
+
+ /* Get root clock */
+ clk = spdif_priv->txclk[STC_TXCLK_SPDIF_ROOT];
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "no rxtx1 clock in devicetree\n");
+ return PTR_ERR(clk);
+ }
+
+ /* Disable clock first, for it was enabled by pm_runtime */
+ clk_disable_unprepare(clk);
+ fsl_asoc_reparent_pll_clocks(dev, clk, spdif_priv->pll8k_clk,
+ spdif_priv->pll11k_clk, ratio);
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -659,6 +702,13 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
int ret = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = spdif_reparent_rootclk(spdif_priv, sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: reparent root clk failed: %d\n",
+ __func__, sample_rate);
+ return ret;
+ }
+
ret = spdif_set_sample_rate(substream, sample_rate);
if (ret) {
dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
@@ -1548,11 +1598,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)
}
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
- for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
- ret = fsl_spdif_probe_txclk(spdif_priv, i);
- if (ret)
- return ret;
- }
+ fsl_asoc_get_pll_clocks(&pdev->dev, &spdif_priv->pll8k_clk,
+ &spdif_priv->pll11k_clk);
/* Initial spinlock for control data */
ctrl = &spdif_priv->fsl_spdif_control;