diff options
| -rw-r--r-- | Documentation/devicetree/bindings/sound/fsl,spdif.yaml | 4 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/sound/fsl-sai.txt | 3 | ||||
| -rw-r--r-- | sound/soc/fsl/Kconfig | 3 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_micfil.c | 31 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_sai.c | 38 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_sai.h | 2 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_spdif.c | 48 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_utils.c | 69 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_utils.h | 7 | 
9 files changed, 200 insertions, 5 deletions
| diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.yaml b/Documentation/devicetree/bindings/sound/fsl,spdif.yaml index f226ec13167a..1d64e8337aa4 100644 --- a/Documentation/devicetree/bindings/sound/fsl,spdif.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.yaml @@ -58,6 +58,8 @@ properties:            slave of the Shared Peripheral Bus and when two or more bus masters            (CPU, DMA or DSP) try to access it. This property is optional depending            on the SoC design. +      - description: PLL clock source for 8kHz series rate, optional. +      - description: PLL clock source for 11khz series rate, optional.      minItems: 9    clock-names: @@ -72,6 +74,8 @@ properties:        - const: rxtx6        - const: rxtx7        - const: spba +      - const: pll8k +      - const: pll11k      minItems: 9    big-endian: diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 4c66e6a1a533..fbdefc3fade7 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -21,6 +21,9 @@ Required properties:    - clock-names		: Must include the "bus" for register access and  			  "mclk1", "mclk2", "mclk3" for bit clock and frame  			  clock providing. +                          "pll8k", "pll11k" are optional, they are the clock +                          source for root clock, one is for 8kHz series rates +                          another one is for 11kHz series rates.    - dmas		: Generic dma devicetree binding as described in  			  Documentation/devicetree/bindings/dma/dma.txt. diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 10fa38753453..614eceda6b9e 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -19,6 +19,7 @@ config SND_SOC_FSL_SAI  	select REGMAP_MMIO  	select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n  	select SND_SOC_GENERIC_DMAENGINE_PCM +	select SND_SOC_FSL_UTILS  	help  	  Say Y if you want to add Synchronous Audio Interface (SAI)  	  support for the Freescale CPUs. @@ -59,6 +60,7 @@ config SND_SOC_FSL_SPDIF  	select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n  	select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC)  	select BITREVERSE +	select SND_SOC_FSL_UTILS  	help  	  Say Y if you want to add Sony/Philips Digital Interface (SPDIF)  	  support for the Freescale CPUs. @@ -80,6 +82,7 @@ config SND_SOC_FSL_MICFIL  	select REGMAP_MMIO  	select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n  	select SND_SOC_GENERIC_DMAENGINE_PCM +	select SND_SOC_FSL_UTILS  	help  	  Say Y if you want to add Pulse Density Modulation microphone  	  interface (MICFIL) support for NXP. diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index be9781ad8849..79ef4e269bc9 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -24,6 +24,7 @@  #include <sound/core.h>  #include "fsl_micfil.h" +#include "fsl_utils.h"  #define MICFIL_OSR_DEFAULT	16 @@ -42,6 +43,8 @@ struct fsl_micfil {  	const struct fsl_micfil_soc_data *soc;  	struct clk *busclk;  	struct clk *mclk; +	struct clk *pll8k_clk; +	struct clk *pll11k_clk;  	struct snd_dmaengine_dai_dma_data dma_params_rx;  	struct sdma_peripheral_config sdmacfg;  	unsigned int dataline; @@ -264,6 +267,27 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,  	return 0;  } +static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int sample_rate) +{ +	struct device *dev = &micfil->pdev->dev; +	u64 ratio = sample_rate; +	struct clk *clk; +	int ret; + +	/* Get root clock */ +	clk = micfil->mclk; + +	/* Disable clock first, for it was enabled by pm_runtime */ +	clk_disable_unprepare(clk); +	fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, +				     micfil->pll11k_clk, ratio); +	ret = clk_prepare_enable(clk); +	if (ret) +		return ret; + +	return 0; +} +  static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,  				struct snd_pcm_hw_params *params,  				struct snd_soc_dai *dai) @@ -287,6 +311,10 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,  	if (ret)  		return ret; +	ret = fsl_micfil_reparent_rootclk(micfil, rate); +	if (ret) +		return ret; +  	ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);  	if (ret)  		return ret; @@ -591,6 +619,9 @@ static int fsl_micfil_probe(struct platform_device *pdev)  		return PTR_ERR(micfil->busclk);  	} +	fsl_asoc_get_pll_clocks(&pdev->dev, &micfil->pll8k_clk, +				&micfil->pll11k_clk); +  	/* init regmap */  	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);  	if (IS_ERR(regs)) diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a0ddaf7e9f60..974ba0780b19 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -23,6 +23,7 @@  #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>  #include "fsl_sai.h" +#include "fsl_utils.h"  #include "imx-pcm.h"  #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ @@ -220,14 +221,48 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,  	return 0;  } +static int fsl_sai_set_mclk_rate(struct snd_soc_dai *dai, int clk_id, unsigned int freq) +{ +	struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); +	int ret; + +	fsl_asoc_reparent_pll_clocks(dai->dev, sai->mclk_clk[clk_id], +				     sai->pll8k_clk, sai->pll11k_clk, freq); + +	ret = clk_set_rate(sai->mclk_clk[clk_id], freq); +	if (ret < 0) +		dev_err(dai->dev, "failed to set clock rate (%u): %d\n", freq, ret); + +	return ret; +} +  static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,  		int clk_id, unsigned int freq, int dir)  { +	struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);  	int ret;  	if (dir == SND_SOC_CLOCK_IN)  		return 0; +	if (freq > 0 && clk_id != FSL_SAI_CLK_BUS) { +		if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) { +			dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id); +			return -EINVAL; +		} + +		if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) { +			dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id); +			return -EINVAL; +		} + +		if (sai->mclk_streams == 0) { +			ret = fsl_sai_set_mclk_rate(cpu_dai, clk_id, freq); +			if (ret < 0) +				return ret; +		} +	} +  	ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);  	if (ret) {  		dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); @@ -1281,6 +1316,9 @@ static int fsl_sai_probe(struct platform_device *pdev)  	else  		sai->mclk_clk[0] = sai->bus_clk; +	fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk, +				&sai->pll11k_clk); +  	/* read dataline mask for rx and tx*/  	ret = fsl_sai_read_dlcfg(sai);  	if (ret < 0) { diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 9bb8ced520c8..17956b5731dc 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -273,6 +273,8 @@ struct fsl_sai {  	struct regmap *regmap;  	struct clk *bus_clk;  	struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; +	struct clk *pll8k_clk; +	struct clk *pll11k_clk;  	struct resource *res;  	bool is_consumer_mode; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 0504431792cf..7fc1c96929bb 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -23,6 +23,7 @@  #include <sound/soc.h>  #include "fsl_spdif.h" +#include "fsl_utils.h"  #include "imx-pcm.h"  #define FSL_SPDIF_TXFIFO_WML	0x8 @@ -114,6 +115,8 @@ struct spdif_mixer_control {   * @dma_params_rx: DMA parameters for receive channel   * @regcache_srpc: regcache for SRPC   * @bypass: status of bypass input to output + * @pll8k_clk: PLL clock for the rate of multiply of 8kHz + * @pll11k_clk: PLL clock for the rate of multiply of 11kHz   */  struct fsl_spdif_priv {  	const struct fsl_spdif_soc_data *soc; @@ -137,6 +140,8 @@ struct fsl_spdif_priv {  	/* regcache for SRPC */  	u32 regcache_srpc;  	bool bypass; +	struct clk *pll8k_clk; +	struct clk *pll11k_clk;  };  static struct fsl_spdif_soc_data fsl_spdif_vf610 = { @@ -480,6 +485,8 @@ static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,  	return 0;  } +static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index); +  static int spdif_set_sample_rate(struct snd_pcm_substream *substream,  				int sample_rate)  { @@ -528,6 +535,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,  		return -EINVAL;  	} +	ret = fsl_spdif_probe_txclk(spdif_priv, rate); +	if (ret) +		return ret; +  	clk = spdif_priv->txclk_src[rate];  	if (clk >= STC_TXCLK_SRC_MAX) {  		dev_err(&pdev->dev, "tx clock source is out of range\n"); @@ -647,6 +658,29 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,  	}  } +static int spdif_reparent_rootclk(struct fsl_spdif_priv *spdif_priv, unsigned int sample_rate) +{ +	struct platform_device *pdev = spdif_priv->pdev; +	struct clk *clk; +	int ret; + +	/* Reparent clock if required condition is true */ +	if (!fsl_spdif_can_set_clk_rate(spdif_priv, STC_TXCLK_SPDIF_ROOT)) +		return 0; + +	/* Get root clock */ +	clk = spdif_priv->txclk[STC_TXCLK_SPDIF_ROOT]; + +	/* Disable clock first, for it was enabled by pm_runtime */ +	clk_disable_unprepare(clk); +	fsl_asoc_reparent_pll_clocks(&pdev->dev, clk, spdif_priv->pll8k_clk, +				     spdif_priv->pll11k_clk, sample_rate); +	ret = clk_prepare_enable(clk); +	if (ret) +		return ret; + +	return 0; +}  static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,  				struct snd_pcm_hw_params *params,  				struct snd_soc_dai *dai) @@ -659,6 +693,13 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,  	int ret = 0;  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		ret = spdif_reparent_rootclk(spdif_priv, sample_rate); +		if (ret) { +			dev_err(&pdev->dev, "%s: reparent root clk failed: %d\n", +				__func__, sample_rate); +			return ret; +		} +  		ret  = spdif_set_sample_rate(substream, sample_rate);  		if (ret) {  			dev_err(&pdev->dev, "%s: set sample rate failed: %d\n", @@ -1548,11 +1589,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)  	}  	spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC; -	for (i = 0; i < SPDIF_TXRATE_MAX; i++) { -		ret = fsl_spdif_probe_txclk(spdif_priv, i); -		if (ret) -			return ret; -	} +	fsl_asoc_get_pll_clocks(&pdev->dev, &spdif_priv->pll8k_clk, +				&spdif_priv->pll11k_clk);  	/* Initial spinlock for control data */  	ctrl = &spdif_priv->fsl_spdif_control; diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 9bab202569af..b75843e31f00 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -6,6 +6,8 @@  //  // Copyright 2010 Freescale Semiconductor, Inc. +#include <linux/clk.h> +#include <linux/clk-provider.h>  #include <linux/module.h>  #include <linux/of_address.h>  #include <sound/soc.h> @@ -83,6 +85,73 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,  }  EXPORT_SYMBOL(fsl_asoc_get_dma_channel); +/** + * fsl_asoc_get_pll_clocks - get two PLL clock source + * + * @dev: device pointer + * @pll8k_clk: PLL clock pointer for 8kHz + * @pll11k_clk: PLL clock pointer for 11kHz + * + * This function get two PLL clock source + */ +void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk, +			     struct clk **pll11k_clk) +{ +	*pll8k_clk = devm_clk_get(dev, "pll8k"); +	if (IS_ERR(*pll8k_clk)) +		*pll8k_clk = NULL; + +	*pll11k_clk = devm_clk_get(dev, "pll11k"); +	if (IS_ERR(*pll11k_clk)) +		*pll11k_clk = NULL; +} +EXPORT_SYMBOL(fsl_asoc_get_pll_clocks); + +/** + * fsl_asoc_reparent_pll_clocks - set clock parent if necessary + * + * @dev: device pointer + * @clk: root clock pointer + * @pll8k_clk: PLL clock pointer for 8kHz + * @pll11k_clk: PLL clock pointer for 11kHz + * @ratio: target requency for root clock + * + * This function set root clock parent according to the target ratio + */ +void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, +				  struct clk *pll8k_clk, +				  struct clk *pll11k_clk, u64 ratio) +{ +	struct clk *p, *pll = 0, *npll = 0; +	bool reparent = false; +	int ret = 0; + +	if (!clk || !pll8k_clk || !pll11k_clk) +		return; + +	p = clk; +	while (p && pll8k_clk && pll11k_clk) { +		struct clk *pp = clk_get_parent(p); + +		if (clk_is_match(pp, pll8k_clk) || +		    clk_is_match(pp, pll11k_clk)) { +			pll = pp; +			break; +		} +		p = pp; +	} + +	npll = (do_div(ratio, 8000) ? pll11k_clk : pll8k_clk); +	reparent = (pll && !clk_is_match(pll, npll)); + +	if (reparent) { +		ret = clk_set_parent(p, npll); +		if (ret < 0) +			dev_warn(dev, "failed to set parent %s: %d\n", __clk_get_name(npll), ret); +	} +} +EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); +  MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");  MODULE_DESCRIPTION("Freescale ASoC utility code");  MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index c5dc2a14b492..4d5f3d93bc81 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -19,4 +19,11 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,  			     struct snd_soc_dai_link *dai,  			     unsigned int *dma_channel_id,  			     unsigned int *dma_id); + +void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk, +			     struct clk **pll11k_clk); + +void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, +				  struct clk *pll8k_clk, +				  struct clk *pll11k_clk, u64 ratio);  #endif /* _FSL_UTILS_H */ | 
