summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_spdif.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_spdif.c')
-rw-r--r--sound/soc/fsl/fsl_spdif.c303
1 files changed, 199 insertions, 104 deletions
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 59214b148e28..eedf20e03a3f 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -11,6 +11,7 @@
#include <linux/bitrev.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -120,6 +121,9 @@ struct fsl_spdif_priv {
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* regcache for SRPC */
u32 regcache_srpc;
+ struct clk *pll8k_clk;
+ struct clk *pll11k_clk;
+ u8 streams;
};
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
@@ -602,6 +606,9 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
}
+ if (!ret)
+ spdif_priv->streams |= BIT(substream->stream);
+
return ret;
}
@@ -635,14 +642,197 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
return 0;
}
+static int fsl_spdif_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+ spdif_priv->streams &= ~BIT(substream->stream);
+
+ return 0;
+}
+
+static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+ struct clk *clk, u64 savesub,
+ enum spdif_txrate index, bool round)
+{
+ static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
+ bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
+ u64 rate_actual, sub;
+ u32 arate;
+ u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
+ u8 txclk_df;
+
+ /* The sysclk has an extra divisor [2, 512] */
+ sysclk_dfmin = is_sysclk ? 2 : 1;
+ sysclk_dfmax = is_sysclk ? 512 : 1;
+
+ for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
+ for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
+
+ rate_actual = clk_get_rate(clk);
+
+ arate = rate_actual / 64;
+ arate /= txclk_df * sysclk_df;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
+ goto out;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (u64)(arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
+ savesub = sub;
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (u64)(rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub >= savesub)
+ continue;
+ savesub = sub;
+ spdif_priv->txclk_df[index] = txclk_df;
+ spdif_priv->sysclk_df[index] = sysclk_df;
+ spdif_priv->txrate[index] = arate;
+ }
+ }
+ }
+
+out:
+ return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_txrate index)
+{
+ static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct device *dev = &pdev->dev;
+ u64 savesub = 100000, ret;
+ struct clk *clk;
+ int i;
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ clk = spdif_priv->txclk[i];
+ if (IS_ERR(clk)) {
+ dev_err(dev, "no rxtx%d clock in devicetree\n", i);
+ return PTR_ERR(clk);
+ }
+ if (!clk_get_rate(clk))
+ continue;
+
+ ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
+ i == STC_TXCLK_SPDIF_ROOT);
+ if (savesub == ret)
+ continue;
+
+ savesub = ret;
+ spdif_priv->txclk_src[index] = i;
+
+ /* To quick catch a divisor, we allow a 0.1% deviation */
+ if (savesub < 100)
+ break;
+ }
+
+ dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
+ spdif_priv->txclk_src[index], rate[index]);
+ dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
+ spdif_priv->txclk_df[index], rate[index]);
+ if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk))
+ dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
+ spdif_priv->sysclk_df[index], rate[index]);
+ dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
+ rate[index], spdif_priv->txrate[index]);
+
+ return 0;
+}
+
+static int fsl_spdif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct fsl_spdif_priv *data = snd_soc_dai_get_drvdata(cpu_dai);
+ struct platform_device *pdev = data->pdev;
+ struct device *dev = &pdev->dev;
+ struct clk *clk, *p, *pll = 0, *npll = 0;
+ u64 ratio = freq;
+ int ret, i;
+
+ if (dir != SND_SOC_CLOCK_OUT || freq == 0 || clk_id != STC_TXCLK_SPDIF_ROOT)
+ return 0;
+
+ if (data->pll8k_clk == NULL || data->pll11k_clk == NULL)
+ return 0;
+
+ clk = data->txclk[clk_id];
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "no rxtx%d clock in devicetree\n", clk_id);
+ return PTR_ERR(clk);
+ }
+
+ p = clk;
+ while (p && data->pll8k_clk && data->pll11k_clk) {
+ struct clk *pp = clk_get_parent(p);
+
+ if (clk_is_match(pp, data->pll8k_clk) ||
+ clk_is_match(pp, data->pll11k_clk)) {
+ pll = pp;
+ break;
+ }
+ p = pp;
+ }
+
+ if (pll) {
+ npll = (do_div(ratio, 8000) ? data->pll11k_clk : data->pll8k_clk);
+ if (!clk_is_match(pll, npll)) {
+ if (!data->streams) {
+ ret = clk_set_parent(p, npll);
+ if (ret < 0)
+ dev_warn(cpu_dai->dev,
+ "failed to set parent %s: %d\n",
+ __clk_get_name(npll), ret);
+ } else {
+ dev_err(cpu_dai->dev,
+ "PLL %s is in use by a running stream.\n",
+ __clk_get_name(pll));
+ return -EINVAL;
+ }
+ }
+ }
+
+ ret = clk_set_rate(clk, freq);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "failed to set clock rate (%u): %d\n",
+ freq, ret);
+ return ret;
+ }
+
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+ ret = fsl_spdif_probe_txclk(data, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops fsl_spdif_dai_ops = {
.startup = fsl_spdif_startup,
+ .set_sysclk = fsl_spdif_set_dai_sysclk,
.hw_params = fsl_spdif_hw_params,
.trigger = fsl_spdif_trigger,
.shutdown = fsl_spdif_shutdown,
+ .hw_free = fsl_spdif_hw_free,
};
-
/*
* FSL SPDIF IEC958 controller(mixer) functions
*
@@ -1174,109 +1364,6 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
-static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
- struct clk *clk, u64 savesub,
- enum spdif_txrate index, bool round)
-{
- static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
- bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
- u64 rate_ideal, rate_actual, sub;
- u32 arate;
- u16 sysclk_dfmin, sysclk_dfmax, sysclk_df;
- u8 txclk_df;
-
- /* The sysclk has an extra divisor [2, 512] */
- sysclk_dfmin = is_sysclk ? 2 : 1;
- sysclk_dfmax = is_sysclk ? 512 : 1;
-
- for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
- for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
-
- rate_actual = clk_get_rate(clk);
-
- arate = rate_actual / 64;
- arate /= txclk_df * sysclk_df;
-
- if (arate == rate[index]) {
- /* We are lucky */
- savesub = 0;
- spdif_priv->txclk_df[index] = txclk_df;
- spdif_priv->sysclk_df[index] = sysclk_df;
- spdif_priv->txrate[index] = arate;
- goto out;
- } else if (arate / rate[index] == 1) {
- /* A little bigger than expect */
- sub = (u64)(arate - rate[index]) * 100000;
- do_div(sub, rate[index]);
- if (sub >= savesub)
- continue;
- savesub = sub;
- spdif_priv->txclk_df[index] = txclk_df;
- spdif_priv->sysclk_df[index] = sysclk_df;
- spdif_priv->txrate[index] = arate;
- } else if (rate[index] / arate == 1) {
- /* A little smaller than expect */
- sub = (u64)(rate[index] - arate) * 100000;
- do_div(sub, rate[index]);
- if (sub >= savesub)
- continue;
- savesub = sub;
- spdif_priv->txclk_df[index] = txclk_df;
- spdif_priv->sysclk_df[index] = sysclk_df;
- spdif_priv->txrate[index] = arate;
- }
- }
- }
-
-out:
- return savesub;
-}
-
-static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
- enum spdif_txrate index)
-{
- static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
- struct platform_device *pdev = spdif_priv->pdev;
- struct device *dev = &pdev->dev;
- u64 savesub = 100000, ret;
- struct clk *clk;
- int i;
-
- for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
- clk = spdif_priv->txclk[i];
- if (IS_ERR(clk)) {
- dev_err(dev, "no rxtx%d clock in devicetree\n", i);
- return PTR_ERR(clk);
- }
- if (!clk_get_rate(clk))
- continue;
-
- ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
- i == STC_TXCLK_SPDIF_ROOT);
- if (savesub == ret)
- continue;
-
- savesub = ret;
- spdif_priv->txclk_src[index] = i;
-
- /* To quick catch a divisor, we allow a 0.1% deviation */
- if (savesub < 100)
- break;
- }
-
- dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
- spdif_priv->txclk_src[index], rate[index]);
- dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
- spdif_priv->txclk_df[index], rate[index]);
- if (clk_is_match(spdif_priv->txclk[spdif_priv->txclk_src[index]], spdif_priv->sysclk))
- dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
- spdif_priv->sysclk_df[index], rate[index]);
- dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
- rate[index], spdif_priv->txrate[index]);
-
- return 0;
-}
-
static const struct of_device_id fsl_spdif_dt_ids[] = {
{ .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, },
{ .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, },
@@ -1394,6 +1481,14 @@ static int fsl_spdif_probe(struct platform_device *pdev)
}
spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+ spdif_priv->pll8k_clk = devm_clk_get(&pdev->dev, "pll8k");
+ if (IS_ERR(spdif_priv->pll8k_clk))
+ spdif_priv->pll8k_clk = NULL;
+
+ spdif_priv->pll11k_clk = devm_clk_get(&pdev->dev, "pll11k");
+ if (IS_ERR(spdif_priv->pll11k_clk))
+ spdif_priv->pll11k_clk = NULL;
+
for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
ret = fsl_spdif_probe_txclk(spdif_priv, i);
if (ret)