diff options
Diffstat (limited to 'sound/soc/fsl/fsl_spdif.c')
-rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 186 |
1 files changed, 136 insertions, 50 deletions
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 6452ca83d88..b912d45a2a4 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -13,18 +13,18 @@ * kind, whether express or implied. */ -#include <linux/module.h> +#include <linux/bitrev.h> #include <linux/clk.h> #include <linux/clk-private.h> -#include <linux/bitrev.h> -#include <linux/regmap.h> +#include <linux/module.h> #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_irq.h> +#include <linux/regmap.h> #include <sound/asoundef.h> -#include <sound/soc.h> #include <sound/dmaengine_pcm.h> +#include <sound/soc.h> #include "fsl_spdif.h" #include "imx-pcm.h" @@ -69,17 +69,42 @@ 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; struct platform_device *pdev; struct regmap *regmap; bool dpll_locked; - u8 txclk_div[SPDIF_TXRATE_MAX]; + u16 txrate[SPDIF_TXRATE_MAX]; + 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]; struct clk *rxclk; + struct clk *coreclk; + struct clk *sysclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -349,7 +374,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, sysclk_df; int ret; switch (sample_rate) { @@ -376,25 +401,31 @@ 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; } + 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; + /* - * 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; } +clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", - (64 * sample_rate * div)); + (64 * sample_rate * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", clk_get_rate(spdif_priv->txclk[rate])); @@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, 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); + regmap_update_bits(regmap, REG_SPDIF_STC, + STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df)); + + dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n", + spdif_priv->txrate[rate], sample_rate); return 0; } @@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, /* Reset module and interrupts only for first initialization */ if (!cpu_dai->active) { + ret = clk_prepare_enable(spdif_priv->coreclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable core clock\n"); + return ret; + } + ret = spdif_softreset(spdif_priv); if (ret) { dev_err(&pdev->dev, "failed to soft reset\n"); - return ret; + goto err; } /* Disable all the interrupts */ @@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); return 0; + +err: + clk_disable_unprepare(spdif_priv->coreclk); + + return ret; } static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, @@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, spdif_intr_status_clear(spdif_priv); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER); + clk_disable_unprepare(spdif_priv->coreclk); } } @@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv, clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { /* Get bus clock from system */ - busclk_freq = clk_get_rate(spdif_priv->rxclk); + busclk_freq = clk_get_rate(spdif_priv->sysclk); } /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */ @@ -997,43 +1044,61 @@ 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 }; + bool is_sysclk = clk == spdif_priv->sysclk; u64 rate_ideal, rate_actual, sub; - u32 div, arate; - - for (div = 1; div <= 128; div++) { - rate_ideal = rate[index] * (div + 1) * 64; - rate_actual = clk_round_rate(clk, rate_ideal); - - arate = rate_actual / 64; - arate /= div; - - if (arate == rate[index]) { - /* We are lucky */ - savesub = 0; - spdif_priv->txclk_div[index] = div; - 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; + spdif_priv->txrate[index] = arate; + 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_div[index] = div; - } - } 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->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; + do_div(sub, rate[index]); + if (sub >= savesub) + continue; savesub = sub; - spdif_priv->txclk_div[index] = div; + spdif_priv->txclk_df[index] = txclk_df; + spdif_priv->sysclk_df[index] = sysclk_df; + spdif_priv->txrate[index] = arate; } } } +out: return savesub; } @@ -1058,7 +1123,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; @@ -1073,8 +1139,13 @@ 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]); + 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; } @@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } + /* Get system clock for rx clock rate calculation */ + spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5"); + if (IS_ERR(spdif_priv->sysclk)) { + dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n"); + return PTR_ERR(spdif_priv->sysclk); + } + + /* Get core clock for data register access via DMA */ + spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(spdif_priv->coreclk)) { + dev_err(&pdev->dev, "no core clock in devicetree\n"); + return PTR_ERR(spdif_priv->coreclk); + } + /* Select clock source for rx/tx clock */ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); if (IS_ERR(spdif_priv->rxclk)) { @@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, + { .compatible = "fsl,vf610-spdif", }, {} }; MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); |