diff options
Diffstat (limited to 'sound/soc/fsl/fsl_spdif.c')
| -rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 233 | 
1 files changed, 167 insertions, 66 deletions
| diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1b2e516f9162..455f96908377 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -16,6 +16,7 @@  #include <linux/of_device.h>  #include <linux/of_irq.h>  #include <linux/regmap.h> +#include <linux/pm_runtime.h>  #include <sound/asoundef.h>  #include <sound/dmaengine_pcm.h> @@ -42,6 +43,18 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };  #define DEFAULT_RXCLK_SRC	1 +/** + * struct fsl_spdif_soc_data: soc specific data + * + * @imx: for imx platform + * @shared_root_clock: flag of sharing a clock source with others; + *                     so the driver shouldn't set root clock rate + */ +struct fsl_spdif_soc_data { +	bool imx; +	bool shared_root_clock; +}; +  /*   * SPDIF control structure   * Defines channel status, subcode and Q sub @@ -68,8 +81,8 @@ struct spdif_mixer_control {  };  /** - * fsl_spdif_priv: Freescale SPDIF private data - * + * struct fsl_spdif_priv - Freescale SPDIF private data + * @soc: SPDIF soc data   * @fsl_spdif_control: SPDIF control data   * @cpu_dai_drv: cpu dai driver   * @pdev: platform device pointer @@ -87,8 +100,10 @@ struct spdif_mixer_control {   * @spbaclk: SPBA clock (optional, depending on SoC design)   * @dma_params_tx: DMA parameters for transmit channel   * @dma_params_rx: DMA parameters for receive channel + * @regcache_srpc: regcache for SRPC   */  struct fsl_spdif_priv { +	const struct fsl_spdif_soc_data *soc;  	struct spdif_mixer_control fsl_spdif_control;  	struct snd_soc_dai_driver cpu_dai_drv;  	struct platform_device *pdev; @@ -110,6 +125,27 @@ struct fsl_spdif_priv {  	u32 regcache_srpc;  }; +static struct fsl_spdif_soc_data fsl_spdif_vf610 = { +	.imx = false, +	.shared_root_clock = false, +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx35 = { +	.imx = true, +	.shared_root_clock = false, +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx6sx = { +	.imx = true, +	.shared_root_clock = true, +}; + +/* Check if clk is a root clock that does not share clock source with others */ +static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk) +{ +	return (clk == STC_TXCLK_SPDIF_ROOT) && !spdif->soc->shared_root_clock; +} +  /* DPLL locked and lock loss interrupt handler */  static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)  { @@ -369,7 +405,7 @@ static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,  static int spdif_set_sample_rate(struct snd_pcm_substream *substream,  				int sample_rate)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);  	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));  	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;  	struct regmap *regmap = spdif_priv->regmap; @@ -420,8 +456,7 @@ 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) +	if (!fsl_spdif_can_set_clk_rate(spdif_priv, clk))  		goto clk_set_bypass;  	/* The S/PDIF block needs a clock of 64 * fs * txclk_df */ @@ -457,34 +492,19 @@ clk_set_bypass:  static int fsl_spdif_startup(struct snd_pcm_substream *substream,  			     struct snd_soc_dai *cpu_dai)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);  	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));  	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 (!snd_soc_dai_active(cpu_dai)) { -		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"); -			goto err; +			return ret;  		}  		/* Disable all the interrupts */ @@ -498,18 +518,10 @@ 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); @@ -517,39 +529,25 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,  	regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);  	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;  }  static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,  				struct snd_soc_dai *cpu_dai)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);  	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));  	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 +556,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);  	}  } @@ -568,7 +563,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,  				struct snd_pcm_hw_params *params,  				struct snd_soc_dai *dai)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);  	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));  	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;  	struct platform_device *pdev = spdif_priv->pdev; @@ -596,7 +591,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,  static int fsl_spdif_trigger(struct snd_pcm_substream *substream,  				int cmd, struct snd_soc_dai *dai)  { -	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);  	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));  	struct regmap *regmap = spdif_priv->regmap;  	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -781,8 +776,8 @@ static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,  }  /* Get valid good bit from interrupt status register */ -static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, -				struct snd_ctl_elem_value *ucontrol) +static int fsl_spdif_rx_vbit_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); @@ -796,6 +791,35 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,  	return 0;  } +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); +	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_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; +} +  /* DPLL lock information */  static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,  				struct snd_ctl_elem_info *uinfo) @@ -953,11 +977,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, +		.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 = fsl_spdif_vbit_info, +		.get = fsl_spdif_tx_vbit_get, +		.put = fsl_spdif_tx_vbit_put,  	},  	/* DPLL lock info get controller */  	{ @@ -990,6 +1024,10 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)  	snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls)); +	/*Clear the val bit for Tx*/ +	regmap_update_bits(spdif_private->regmap, REG_SPDIF_SCR, +			   SCR_VAL_MASK, SCR_VAL_CLEAR); +  	return 0;  } @@ -1186,7 +1224,7 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,  			continue;  		ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index, -					     i == STC_TXCLK_SPDIF_ROOT); +					     fsl_spdif_can_set_clk_rate(spdif_priv, i));  		if (savesub == ret)  			continue; @@ -1230,6 +1268,12 @@ static int fsl_spdif_probe(struct platform_device *pdev)  	spdif_priv->pdev = pdev; +	spdif_priv->soc = of_device_get_match_data(&pdev->dev); +	if (!spdif_priv->soc) { +		dev_err(&pdev->dev, "failed to get soc data\n"); +		return -ENODEV; +	} +  	/* 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); @@ -1311,6 +1355,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)  	/* Register with ASoC */  	dev_set_drvdata(&pdev->dev, spdif_priv); +	pm_runtime_enable(&pdev->dev); +	regcache_cache_only(spdif_priv->regmap, true);  	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_spdif_component,  					      &spdif_priv->cpu_dai_drv, 1); @@ -1326,41 +1372,96 @@ 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_suspend(struct device *dev)  {  	struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); +	int i;  	regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,  			&spdif_priv->regcache_srpc); -  	regcache_cache_only(spdif_priv->regmap, true); -	regcache_mark_dirty(spdif_priv->regmap); + +	clk_disable_unprepare(spdif_priv->rxclk); + +	for (i = 0; i < SPDIF_TXRATE_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;  } -static int fsl_spdif_resume(struct device *dev) +static int fsl_spdif_runtime_resume(struct device *dev)  {  	struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev); +	int ret; +	int i; + +	ret = clk_prepare_enable(spdif_priv->coreclk); +	if (ret) { +		dev_err(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(dev, "failed to enable spba clock\n"); +			goto disable_core_clk; +		} +	} + +	for (i = 0; i < SPDIF_TXRATE_MAX; i++) { +		ret = clk_prepare_enable(spdif_priv->txclk[i]); +		if (ret) +			goto disable_tx_clk; +	} + +	ret = clk_prepare_enable(spdif_priv->rxclk); +	if (ret) +		goto disable_tx_clk;  	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); -	return regcache_sync(spdif_priv->regmap); +	ret = regcache_sync(spdif_priv->regmap); +	if (ret) +		goto disable_rx_clk; + +	return 0; + +disable_rx_clk: +	clk_disable_unprepare(spdif_priv->rxclk); +disable_tx_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;  } -#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM */  static const struct dev_pm_ops fsl_spdif_pm = { -	SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume) +	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)  };  static const struct of_device_id fsl_spdif_dt_ids[] = { -	{ .compatible = "fsl,imx35-spdif", }, -	{ .compatible = "fsl,vf610-spdif", }, +	{ .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, }, +	{ .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, }, +	{ .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, },  	{}  };  MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); | 
