[alsa-devel] [PATCH 0/5] ASoC: fsl_spdif: Add sysclk df support
This series of patches mainly add sysclk_df support to derive tx clock from sysclk, and meanwhile, accordingly do some additional improvements.
Nicolin Chen (5): ASoC: fsl_spdif: Use clk_set_rate() for spdif root clock only ASoC: fsl_spdif: Rename all _div to _df ASoC: fsl_spdif: Add sysclk df support to derive txclk from sysclk ASoC: fsl_spdif: Print actual sample rate for debug ASoc: fsl_spdif: Add descriptions for fsl_spdif_priv
sound/soc/fsl/fsl_spdif.c | 145 ++++++++++++++++++++++++++++++++-------------- sound/soc/fsl/fsl_spdif.h | 14 +++-- 2 files changed, 109 insertions(+), 50 deletions(-)
The clock mux for the Freescale S/PDIF controller has eight clock sources while most of them are from other moudles and even system clocks that do not allow a rate-changing operation.
So we here only allow the clk_set_rate() and clk_round_rate() happened to spdif root clock, the private clock for S/PDIF controller.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com --- sound/soc/fsl/fsl_spdif.c | 15 ++++++++++++--- sound/soc/fsl/fsl_spdif.h | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index e7be6b3..ed4bf75 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -384,6 +384,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return -EINVAL; }
+ /* Don't mess up the clocks from other modules */ + if (clk != STC_TXCLK_SPDIF_ROOT) + goto clk_set_bypass; + /* * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block * will divide by (div). So request 64 * fs * (div+1) which will @@ -395,6 +399,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return ret; }
+clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", (64 * sample_rate * div)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", @@ -1011,7 +1016,7 @@ static struct regmap_config fsl_spdif_regmap_config = {
static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, - enum spdif_txrate index) + enum spdif_txrate index, bool round) { const u32 rate[] = { 32000, 44100, 48000 }; u64 rate_ideal, rate_actual, sub; @@ -1019,7 +1024,10 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
for (div = 1; div <= 128; div++) { rate_ideal = rate[index] * (div + 1) * 64; - rate_actual = clk_round_rate(clk, rate_ideal); + if (round) + rate_actual = clk_round_rate(clk, rate_ideal); + else + rate_actual = clk_get_rate(clk);
arate = rate_actual / 64; arate /= div; @@ -1072,7 +1080,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, if (!clk_get_rate(clk)) continue;
- ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index); + ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index, + i == STC_TXCLK_SPDIF_ROOT); if (savesub == ret) continue;
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 605a10b..4ec27fc 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -157,6 +157,8 @@ enum spdif_gainsel { #define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) #define STC_TXCLK_SRC_MAX 8
+#define STC_TXCLK_SPDIF_ROOT 1 + /* SPDIF tx rate */ enum spdif_txrate { SPDIF_TXRATE_32000 = 0,
On Wed, Apr 30, 2014 at 06:54:05PM +0800, Nicolin Chen wrote:
The clock mux for the Freescale S/PDIF controller has eight clock sources while most of them are from other moudles and even system clocks that do not allow a rate-changing operation.
Applied, thanks.
We should have used _df by following the reference manual at the beginning. So this patch just renames them.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com --- sound/soc/fsl/fsl_spdif.c | 41 ++++++++++++++++++++--------------------- sound/soc/fsl/fsl_spdif.h | 12 ++++++------ 2 files changed, 26 insertions(+), 27 deletions(-)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index ed4bf75..af8bc37 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -75,7 +75,7 @@ struct fsl_spdif_priv { struct platform_device *pdev; struct regmap *regmap; bool dpll_locked; - u8 txclk_div[SPDIF_TXRATE_MAX]; + u8 txclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; struct clk *txclk[SPDIF_TXRATE_MAX]; @@ -351,7 +351,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; unsigned long csfs = 0; u32 stc, mask, rate; - u8 clk, div; + u8 clk, txclk_df; int ret;
switch (sample_rate) { @@ -378,9 +378,9 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return -EINVAL; }
- div = spdif_priv->txclk_div[rate]; - if (div == 0) { - dev_err(&pdev->dev, "the divisor can't be zero\n"); + txclk_df = spdif_priv->txclk_df[rate]; + if (txclk_df == 0) { + dev_err(&pdev->dev, "the txclk_df can't be zero\n"); return -EINVAL; }
@@ -389,11 +389,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, goto clk_set_bypass;
/* - * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block - * will divide by (div). So request 64 * fs * (div+1) which will - * get rounded. + * The S/PDIF block needs a clock of 64 * fs * txclk_df. + * So request 64 * fs * (txclk_df + 1) to get rounded. */ - ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1)); + ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1)); if (ret) { dev_err(&pdev->dev, "failed to set tx clock rate\n"); return ret; @@ -401,7 +400,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", - (64 * sample_rate * div)); + (64 * sample_rate * txclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", clk_get_rate(spdif_priv->txclk[rate]));
@@ -409,8 +408,8 @@ clk_set_bypass: spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
/* select clock source and divisor */ - stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div); - mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK; + stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df); + mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK; regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate); @@ -1020,22 +1019,22 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, { const u32 rate[] = { 32000, 44100, 48000 }; u64 rate_ideal, rate_actual, sub; - u32 div, arate; + u32 txclk_df, arate;
- for (div = 1; div <= 128; div++) { - rate_ideal = rate[index] * (div + 1) * 64; + for (txclk_df = 1; txclk_df <= 128; txclk_df++) { + rate_ideal = rate[index] * (txclk_df + 1) * 64; if (round) rate_actual = clk_round_rate(clk, rate_ideal); else rate_actual = clk_get_rate(clk);
arate = rate_actual / 64; - arate /= div; + arate /= txclk_df;
if (arate == rate[index]) { /* We are lucky */ savesub = 0; - spdif_priv->txclk_div[index] = div; + spdif_priv->txclk_df[index] = txclk_df; break; } else if (arate / rate[index] == 1) { /* A little bigger than expect */ @@ -1043,7 +1042,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, do_div(sub, rate[index]); if (sub < savesub) { savesub = sub; - spdif_priv->txclk_div[index] = div; + spdif_priv->txclk_df[index] = txclk_df; } } else if (rate[index] / arate == 1) { /* A little smaller than expect */ @@ -1051,7 +1050,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, do_div(sub, rate[index]); if (sub < savesub) { savesub = sub; - spdif_priv->txclk_div[index] = div; + spdif_priv->txclk_df[index] = txclk_df; } } } @@ -1096,8 +1095,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
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 divisor %d for %dHz sample rate\n", - spdif_priv->txclk_div[index], rate[index]); + dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n", + spdif_priv->txclk_df[index], rate[index]);
return 0; } diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 4ec27fc..16fde4b 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -143,18 +143,18 @@ enum spdif_gainsel { #define INT_RXFIFO_FUL (1 << 0)
/* SPDIF Clock register */ -#define STC_SYSCLK_DIV_OFFSET 11 -#define STC_SYSCLK_DIV_MASK (0x1ff << STC_SYSCLK_DIV_OFFSET) -#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK) +#define STC_SYSCLK_DF_OFFSET 11 +#define STC_SYSCLK_DF_MASK (0x1ff << STC_SYSCLK_DF_OFFSET) +#define STC_SYSCLK_DF(x) ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK) #define STC_TXCLK_SRC_OFFSET 8 #define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET) #define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK) #define STC_TXCLK_ALL_EN_OFFSET 7 #define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) #define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET) -#define STC_TXCLK_DIV_OFFSET 0 -#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET) -#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) +#define STC_TXCLK_DF_OFFSET 0 +#define STC_TXCLK_DF_MASK (0x7ff << STC_TXCLK_DF_OFFSET) +#define STC_TXCLK_DF(x) ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK) #define STC_TXCLK_SRC_MAX 8
#define STC_TXCLK_SPDIF_ROOT 1
The sysclk is one the clock sources that could be selected to derive tx clock. But the route for sysclk is a bit different that it does not only contain txclk df divider but also have an extra sysclk df.
So this patch mainly adds syclk df configuration support so as to let the driver be able to get clock from sysclk.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com --- sound/soc/fsl/fsl_spdif.c | 81 +++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 30 deletions(-)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index af8bc37..1abfb00 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -76,6 +76,7 @@ struct fsl_spdif_priv { struct regmap *regmap; bool dpll_locked; u8 txclk_df[SPDIF_TXRATE_MAX]; + u8 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; struct clk *txclk[SPDIF_TXRATE_MAX]; @@ -351,7 +352,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; unsigned long csfs = 0; u32 stc, mask, rate; - u8 clk, txclk_df; + u8 clk, txclk_df, sysclk_df; int ret;
switch (sample_rate) { @@ -384,6 +385,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return -EINVAL; }
+ sysclk_df = spdif_priv->sysclk_df[rate]; + /* Don't mess up the clocks from other modules */ if (clk != STC_TXCLK_SPDIF_ROOT) goto clk_set_bypass; @@ -400,7 +403,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", - (64 * sample_rate * txclk_df)); + (64 * sample_rate * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", clk_get_rate(spdif_priv->txclk[rate]));
@@ -412,6 +415,9 @@ clk_set_bypass: mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK; regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+ regmap_update_bits(regmap, REG_SPDIF_STC, + STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df)); + dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
return 0; @@ -1018,43 +1024,55 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index, bool round) { const u32 rate[] = { 32000, 44100, 48000 }; + bool is_sysclk = clk == spdif_priv->sysclk; u64 rate_ideal, rate_actual, sub; - u32 txclk_df, arate; - - for (txclk_df = 1; txclk_df <= 128; txclk_df++) { - rate_ideal = rate[index] * (txclk_df + 1) * 64; - if (round) - rate_actual = clk_round_rate(clk, rate_ideal); - else - rate_actual = clk_get_rate(clk); - - arate = rate_actual / 64; - arate /= txclk_df; - - if (arate == rate[index]) { - /* We are lucky */ - savesub = 0; - spdif_priv->txclk_df[index] = txclk_df; - break; - } else if (arate / rate[index] == 1) { - /* A little bigger than expect */ - sub = (arate - rate[index]) * 100000; - do_div(sub, rate[index]); - if (sub < savesub) { + u32 sysclk_dfmin, sysclk_dfmax; + u32 txclk_df, sysclk_df, arate; + + /* 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_ideal = rate[index] * (txclk_df + 1) * 64; + if (round) + rate_actual = clk_round_rate(clk, rate_ideal); + else + 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; + goto out; + } else if (arate / rate[index] == 1) { + /* A little bigger than expect */ + sub = (arate - rate[index]) * 100000; + do_div(sub, rate[index]); + if (sub >= savesub) + continue; savesub = sub; spdif_priv->txclk_df[index] = txclk_df; - } - } else if (rate[index] / arate == 1) { - /* A little smaller than expect */ - sub = (rate[index] - arate) * 100000; - do_div(sub, rate[index]); - if (sub < savesub) { + spdif_priv->sysclk_df[index] = sysclk_df; + } else if (rate[index] / arate == 1) { + /* A little smaller than expect */ + sub = (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; } } }
+out: return savesub; }
@@ -1097,6 +1115,9 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, 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 (spdif_priv->txclk[index] == spdif_priv->sysclk) + dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n", + spdif_priv->sysclk_df[index], rate[index]);
return 0; }
On Wed, Apr 30, 2014 at 06:54:07PM +0800, Nicolin Chen wrote:
The sysclk is one the clock sources that could be selected to derive tx clock. But the route for sysclk is a bit different that it does not only contain txclk df divider but also have an extra sysclk df.
Applied, thanks.
People would simply know what the driver gets the best for the current sample rate playback.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com --- sound/soc/fsl/fsl_spdif.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1abfb00..074b355 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -75,6 +75,7 @@ struct fsl_spdif_priv { struct platform_device *pdev; struct regmap *regmap; bool dpll_locked; + u16 txrate[SPDIF_TXRATE_MAX]; u8 txclk_df[SPDIF_TXRATE_MAX]; u8 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; @@ -418,7 +419,8 @@ clk_set_bypass: regmap_update_bits(regmap, REG_SPDIF_STC, STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
- dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate); + dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n", + spdif_priv->txrate[rate], sample_rate);
return 0; } @@ -1049,6 +1051,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, 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 */ @@ -1059,6 +1062,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, 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 = (rate[index] - arate) * 100000; @@ -1068,6 +1072,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, savesub = sub; spdif_priv->txclk_df[index] = txclk_df; spdif_priv->sysclk_df[index] = sysclk_df; + spdif_priv->txrate[index] = arate; } } } @@ -1118,6 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, if (spdif_priv->txclk[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; }
Other people would clearly understand each member and improve if they want.
Signed-off-by: Nicolin Chen Guangyu.Chen@freescale.com --- sound/soc/fsl/fsl_spdif.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 074b355..b912d45 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -69,6 +69,27 @@ struct spdif_mixer_control { u32 ready_buf; };
+/** + * fsl_spdif_priv: Freescale SPDIF private data + * + * @fsl_spdif_control: SPDIF control data + * @cpu_dai_drv: cpu dai driver + * @pdev: platform device pointer + * @regmap: regmap handler + * @dpll_locked: dpll lock flag + * @txrate: the best rates for playback + * @txclk_df: STC_TXCLK_DF dividers value for playback + * @sysclk_df: STC_SYSCLK_DF dividers value for playback + * @txclk_src: STC_TXCLK_SRC values for playback + * @rxclk_src: SRPC_CLKSRC_SEL values for capture + * @txclk: tx clock sources for playback + * @rxclk: rx clock sources for capture + * @coreclk: core clock for register access via DMA + * @sysclk: system clock for rx clock rate measurement + * @dma_params_tx: DMA parameters for transmit channel + * @dma_params_rx: DMA parameters for receive channel + * @name: driver name + */ struct fsl_spdif_priv { struct spdif_mixer_control fsl_spdif_control; struct snd_soc_dai_driver cpu_dai_drv;
participants (2)
-
Mark Brown
-
Nicolin Chen