diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2019-12-02 18:00:44 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2019-12-02 18:00:44 +0800 |
commit | 635de10e168f7b2417494248d54bbd94bec989b3 (patch) | |
tree | 9dc68d9ad910bdb20033074a716be4376025ed56 /sound/soc/fsl/fsl_spdif.c | |
parent | b7f8883066ed37854ab4d87ca6df8f628dfa0126 (diff) | |
parent | c7dad700c4263b990ded932d5524e4307100546c (diff) |
Merge remote-tracking branch 'origin/audio/spdif' into audio/next
* origin/audio/spdif: (20 commits)
LF-106: ASoC: fsl_spdif: request BUS_FREQ_HIGH
ASoC: fsl_spdif:Support multi power domains
ASoC: fsl_spdif: Add pm_runtime_enable in probe
MLK-21484-2: ASoC: fsl_spdif: ensure clk is unprepared before reparent
MLK-19154-5: ASoC: fsl_spdif: refine PLL switch handling
...
Diffstat (limited to 'sound/soc/fsl/fsl_spdif.c')
-rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 682 |
1 files changed, 467 insertions, 215 deletions
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 4842e6df9a2d..08d75e6388ca 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -2,7 +2,7 @@ // // Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver // -// Copyright (C) 2013 Freescale Semiconductor, Inc. +// Copyright (C) 2013-2016 Freescale Semiconductor, Inc. // // Based on stmp3xxx_spdif_dai.c // Vladimir Barinov <vbarinov@embeddedalley.com> @@ -11,11 +11,15 @@ #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> #include <linux/of_irq.h> #include <linux/regmap.h> +#include <linux/pm_runtime.h> +#include <linux/pm_domain.h> +#include <linux/busfreq-imx.h> #include <sound/asoundef.h> #include <sound/dmaengine_pcm.h> @@ -42,6 +46,16 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; #define DEFAULT_RXCLK_SRC 1 +struct fsl_spdif_soc_data { + bool imx; + bool constrain_period_size; + u32 tx_burst; + u32 rx_burst; + u32 interrupts; + u64 tx_formats; + u64 rx_rates; +}; + /* * SPDIF control structure * Defines channel status, subcode and Q sub @@ -99,15 +113,58 @@ struct fsl_spdif_priv { u16 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; u8 rxclk_src; - struct clk *txclk[SPDIF_TXRATE_MAX]; + struct clk *txclk[STC_TXCLK_SRC_MAX]; struct clk *rxclk; struct clk *coreclk; struct clk *sysclk; struct clk *spbaclk; + const struct fsl_spdif_soc_data *soc; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; /* regcache for SRPC */ u32 regcache_srpc; + struct clk *pll8k_clk; + struct clk *pll11k_clk; +}; + +static struct fsl_spdif_soc_data fsl_spdif_vf610 = { + .imx = false, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .interrupts = 1, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, + .rx_rates = FSL_SPDIF_RATES_CAPTURE, + .constrain_period_size = false, +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx35 = { + .imx = true, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .interrupts = 1, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, + .rx_rates = FSL_SPDIF_RATES_CAPTURE, + .constrain_period_size = false, +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx8qm = { + .imx = true, + .tx_burst = 2, + .rx_burst = 2, + .interrupts = 2, + .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, + .rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000), + .constrain_period_size = true, +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx8mm = { + .imx = true, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .interrupts = 1, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, + .rx_rates = (FSL_SPDIF_RATES_CAPTURE | SNDRV_PCM_RATE_192000), + .constrain_period_size = false, }; /* DPLL locked and lock loss interrupt handler */ @@ -378,7 +435,6 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, u32 stc, mask, rate; u16 sysclk_df; u8 clk, txclk_df; - int ret; switch (sample_rate) { case 32000: @@ -420,23 +476,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, 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 * txclk_df */ - ret = clk_set_rate(spdif_priv->txclk[rate], - 64 * sample_rate * txclk_df); - 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 * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", - clk_get_rate(spdif_priv->txclk[rate])); + clk_get_rate(spdif_priv->txclk[clk])); /* set fs field in consumer channel status */ spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); @@ -462,25 +505,10 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, struct platform_device *pdev = spdif_priv->pdev; struct regmap *regmap = spdif_priv->regmap; u32 scr, mask; - int i; int ret; /* 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; - } - - if (!IS_ERR(spdif_priv->spbaclk)) { - ret = clk_prepare_enable(spdif_priv->spbaclk); - if (ret) { - dev_err(&pdev->dev, "failed to enable spba clock\n"); - goto err_spbaclk; - } - } - ret = spdif_softreset(spdif_priv); if (ret) { dev_err(&pdev->dev, "failed to soft reset\n"); @@ -498,35 +526,30 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; - for (i = 0; i < SPDIF_TXRATE_MAX; i++) { - ret = clk_prepare_enable(spdif_priv->txclk[i]); - if (ret) - goto disable_txclk; - } } else { scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; - ret = clk_prepare_enable(spdif_priv->rxclk); - if (ret) - goto err; } regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); /* Power up SPDIF module */ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); + if (spdif_priv->soc->constrain_period_size) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + spdif_priv->dma_params_tx.maxburst); + else + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + spdif_priv->dma_params_rx.maxburst); + } + return 0; -disable_txclk: - for (i--; i >= 0; i--) - clk_disable_unprepare(spdif_priv->txclk[i]); err: - if (!IS_ERR(spdif_priv->spbaclk)) - clk_disable_unprepare(spdif_priv->spbaclk); -err_spbaclk: - clk_disable_unprepare(spdif_priv->coreclk); - return ret; } @@ -536,20 +559,17 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct regmap *regmap = spdif_priv->regmap; - u32 scr, mask, i; + u32 scr, mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { scr = 0; mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | SCR_TXFIFO_FSEL_MASK; - for (i = 0; i < SPDIF_TXRATE_MAX; i++) - clk_disable_unprepare(spdif_priv->txclk[i]); } else { scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; - clk_disable_unprepare(spdif_priv->rxclk); } regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); @@ -558,9 +578,6 @@ 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); - if (!IS_ERR(spdif_priv->spbaclk)) - clk_disable_unprepare(spdif_priv->spbaclk); - clk_disable_unprepare(spdif_priv->coreclk); } } @@ -623,14 +640,178 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, 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; + bool reparent = false; + + 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; + } + + npll = (do_div(ratio, 8000) ? data->pll11k_clk : data->pll8k_clk); + reparent = (pll && !clk_is_match(pll, npll)); + + clk_disable_unprepare(clk); + if (reparent) { + 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); + } + + ret = clk_set_rate(clk, freq); + if (ret < 0) + dev_warn(cpu_dai->dev, "failed to set clock rate (%u): %d\n", + freq, ret); + clk_prepare_enable(clk); + + 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, }; - /* * FSL SPDIF IEC958 controller(mixer) functions * @@ -769,19 +950,23 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, } /* Valid bit information */ -static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +/* Get valid good bit from interrupt status register */ +static int fsl_spdif_rx_vbit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + regmap_read(regmap, REG_SPDIF_SIS, &val); + ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0; + regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD); return 0; } -/* Get valid good bit from interrupt status register */ -static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, +static int fsl_spdif_tx_vbit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); @@ -789,9 +974,56 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, struct regmap *regmap = spdif_priv->regmap; u32 val; - regmap_read(regmap, REG_SPDIF_SIS, &val); - ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0; - regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD); + regmap_read(regmap, REG_SPDIF_SCR, &val); + val = (val & SCR_VAL_MASK) >> SCR_VAL_OFFSET; + val = 1 - val; + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int fsl_spdif_tx_vbit_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val = (1 - ucontrol->value.integer.value[0]) << SCR_VAL_OFFSET; + + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, val); + + return 0; +} + +static int fsl_spdif_rx_rcm_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + regmap_read(regmap, REG_SPDIF_SCR, &val); + val = (val & SCR_RAW_CAPTURE_MODE) ? 1 : 0; + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val = (ucontrol->value.integer.value[0] ? SCR_RAW_CAPTURE_MODE : 0); + + if (val) + cpu_dai->driver->capture.formats |= SNDRV_PCM_FMTBIT_S32_LE; + else + cpu_dai->driver->capture.formats &= ~SNDRV_PCM_FMTBIT_S32_LE; + + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_RAW_CAPTURE_MODE, val); return 0; } @@ -863,18 +1095,6 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol, return 0; } -/* User bit sync mode info */ -static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - - return 0; -} - /* * User bit sync mode: * 1 CD User channel subcode @@ -953,11 +1173,21 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = { /* Valid bit error controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "IEC958 V-Bit Errors", + .name = "IEC958 Rx V-Bit Errors", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .info = fsl_spdif_vbit_info, - .get = fsl_spdif_vbit_get, + .info = snd_ctl_boolean_mono_info, + .get = fsl_spdif_rx_vbit_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Tx V-Bit", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ctl_boolean_mono_info, + .get = fsl_spdif_tx_vbit_get, + .put = fsl_spdif_tx_vbit_put, }, /* DPLL lock info get controller */ { @@ -975,10 +1205,20 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .info = fsl_spdif_usync_info, + .info = snd_ctl_boolean_mono_info, .get = fsl_spdif_usync_get, .put = fsl_spdif_usync_put, }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Rx Raw Capture Mode Bit", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ctl_boolean_mono_info, + .get = fsl_spdif_rx_rcm_get, + .put = fsl_spdif_rx_rcm_put, + }, }; static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) @@ -1103,114 +1343,14 @@ 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_ideal = rate[index] * txclk_df * 64ULL; - 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 = (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; - char tmp[16]; - int i; - - for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { - sprintf(tmp, "rxtx%d", i); - clk = devm_clk_get(&pdev->dev, tmp); - 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[index] = clk; - 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[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, }, + { .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, }, + { .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); static int fsl_spdif_probe(struct platform_device *pdev) { @@ -1218,8 +1358,11 @@ static int fsl_spdif_probe(struct platform_device *pdev) struct fsl_spdif_priv *spdif_priv; struct spdif_mixer_control *ctrl; struct resource *res; + const struct of_device_id *of_id; void __iomem *regs; int irq, ret, i; + char tmp[16]; + int num_domains = 0; if (!np) return -ENODEV; @@ -1230,9 +1373,19 @@ static int fsl_spdif_probe(struct platform_device *pdev) spdif_priv->pdev = pdev; + of_id = of_match_device(fsl_spdif_dt_ids, &pdev->dev); + if (!of_id || !of_id->data) + return -EINVAL; + + spdif_priv->soc = of_id->data; + /* Initialize this copy of the CPU DAI driver structure */ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev); + spdif_priv->cpu_dai_drv.playback.formats = + spdif_priv->soc->tx_formats; + spdif_priv->cpu_dai_drv.capture.rates = + spdif_priv->soc->rx_rates; /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1260,8 +1413,50 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } + if (spdif_priv->soc->interrupts > 1) { + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, + dev_name(&pdev->dev), spdif_priv); + if (ret) { + dev_err(&pdev->dev, "could not claim irq %u\n", irq); + return ret; + } + } + + num_domains = of_count_phandle_with_args(np, "power-domains", + "#power-domain-cells"); + for (i = 0; i < num_domains; i++) { + struct device *pd_dev; + struct device_link *link; + + pd_dev = dev_pm_domain_attach_by_id(&pdev->dev, i); + if (IS_ERR(pd_dev)) + return PTR_ERR(pd_dev); + + link = device_link_add(&pdev->dev, pd_dev, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (IS_ERR(link)) + return PTR_ERR(link); + } + + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { + sprintf(tmp, "rxtx%d", i); + spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(spdif_priv->txclk[i])) { + dev_err(&pdev->dev, "no rxtx%d clock in devicetree\n", i); + return PTR_ERR(spdif_priv->txclk[i]); + } + } + /* Get system clock for rx clock rate calculation */ - spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5"); + spdif_priv->sysclk = spdif_priv->txclk[5]; if (IS_ERR(spdif_priv->sysclk)) { dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n"); return PTR_ERR(spdif_priv->sysclk); @@ -1279,13 +1474,21 @@ static int fsl_spdif_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "no spba clock in devicetree\n"); /* Select clock source for rx/tx clock */ - spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); + spdif_priv->rxclk = spdif_priv->txclk[1]; if (IS_ERR(spdif_priv->rxclk)) { dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n"); return PTR_ERR(spdif_priv->rxclk); } 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) @@ -1306,11 +1509,17 @@ static int fsl_spdif_probe(struct platform_device *pdev) spdif_priv->dpll_locked = false; - spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML; - spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML; + spdif_priv->dma_params_tx.maxburst = spdif_priv->soc->tx_burst; + spdif_priv->dma_params_rx.maxburst = spdif_priv->soc->rx_burst; spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL; spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL; + /*Clear the val bit for Tx*/ + regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SCR, + SCR_VAL_MASK, 1 << SCR_VAL_OFFSET); + + pm_runtime_enable(&pdev->dev); + /* Register with ASoC */ dev_set_drvdata(&pdev->dev, spdif_priv); @@ -1328,44 +1537,87 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } -#ifdef CONFIG_PM_SLEEP -static int fsl_spdif_suspend(struct device *dev) +#ifdef CONFIG_PM +static int fsl_spdif_runtime_resume(struct device *dev) { struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + int ret; + int i; - regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, - &spdif_priv->regcache_srpc); + ret = clk_prepare_enable(spdif_priv->coreclk); + if (ret) { + dev_err(dev, "failed to enable core clock\n"); + return ret; + } - regcache_cache_only(spdif_priv->regmap, true); + if (!IS_ERR(spdif_priv->spbaclk)) { + ret = clk_prepare_enable(spdif_priv->spbaclk); + if (ret) { + dev_err(dev, "failed to enable spba clock\n"); + goto disable_core_clk; + } + } + + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { + ret = clk_prepare_enable(spdif_priv->txclk[i]); + if (ret) + goto disable_spba_clk; + } + + request_bus_freq(BUS_FREQ_HIGH); + + regcache_cache_only(spdif_priv->regmap, false); regcache_mark_dirty(spdif_priv->regmap); + regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + spdif_priv->regcache_srpc); + + ret = regcache_sync(spdif_priv->regmap); + if (ret) + goto disable_tx_clk; + return 0; + +disable_tx_clk: +disable_spba_clk: + for (i--; i >= 0; i--) + clk_disable_unprepare(spdif_priv->txclk[i]); + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); +disable_core_clk: + clk_disable_unprepare(spdif_priv->coreclk); + + return ret; } -static int fsl_spdif_resume(struct device *dev) +static int fsl_spdif_runtime_suspend(struct device *dev) { struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); + int i; - regcache_cache_only(spdif_priv->regmap, false); + regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC, + &spdif_priv->regcache_srpc); + regcache_cache_only(spdif_priv->regmap, true); - regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC, - SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, - spdif_priv->regcache_srpc); + release_bus_freq(BUS_FREQ_HIGH); - return regcache_sync(spdif_priv->regmap); + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) + clk_disable_unprepare(spdif_priv->txclk[i]); + + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); + clk_disable_unprepare(spdif_priv->coreclk); + + return 0; } -#endif /* CONFIG_PM_SLEEP */ +#endif static const struct dev_pm_ops fsl_spdif_pm = { - SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) -}; - -static const struct of_device_id fsl_spdif_dt_ids[] = { - { .compatible = "fsl,imx35-spdif", }, - { .compatible = "fsl,vf610-spdif", }, - {} + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(fsl_spdif_runtime_suspend, fsl_spdif_runtime_resume, + NULL) }; -MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); static struct platform_driver fsl_spdif_driver = { .driver = { |