mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ASoC: stm32: sai: set sai as mclk clock provider
Add master clock generation support in STM32 SAI. The master clock provided by SAI can be used to feed a codec. Signed-off-by: Olivier Moysan <olivier.moysan@st.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
		
							parent
							
								
									1c5083b37d
								
							
						
					
					
						commit
						8307b2afd3
					
				
					 2 changed files with 242 additions and 36 deletions
				
			
		| 
						 | 
					@ -91,6 +91,9 @@
 | 
				
			||||||
#define SAI_XCR1_OSR_SHIFT	26
 | 
					#define SAI_XCR1_OSR_SHIFT	26
 | 
				
			||||||
#define SAI_XCR1_OSR		BIT(SAI_XCR1_OSR_SHIFT)
 | 
					#define SAI_XCR1_OSR		BIT(SAI_XCR1_OSR_SHIFT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SAI_XCR1_MCKEN_SHIFT	27
 | 
				
			||||||
 | 
					#define SAI_XCR1_MCKEN		BIT(SAI_XCR1_MCKEN_SHIFT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/******************* Bit definition for SAI_XCR2 register *******************/
 | 
					/******************* Bit definition for SAI_XCR2 register *******************/
 | 
				
			||||||
#define SAI_XCR2_FTH_SHIFT	0
 | 
					#define SAI_XCR2_FTH_SHIFT	0
 | 
				
			||||||
#define SAI_XCR2_FTH_MASK	GENMASK(2, SAI_XCR2_FTH_SHIFT)
 | 
					#define SAI_XCR2_FTH_MASK	GENMASK(2, SAI_XCR2_FTH_SHIFT)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/clk.h>
 | 
					#include <linux/clk.h>
 | 
				
			||||||
 | 
					#include <linux/clk-provider.h>
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/of_irq.h>
 | 
					#include <linux/of_irq.h>
 | 
				
			||||||
| 
						 | 
					@ -68,6 +69,8 @@
 | 
				
			||||||
#define SAI_IEC60958_BLOCK_FRAMES	192
 | 
					#define SAI_IEC60958_BLOCK_FRAMES	192
 | 
				
			||||||
#define SAI_IEC60958_STATUS_BYTES	24
 | 
					#define SAI_IEC60958_STATUS_BYTES	24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SAI_MCLK_NAME_LEN		32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
 | 
					 * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
 | 
				
			||||||
 * @pdev: device data pointer
 | 
					 * @pdev: device data pointer
 | 
				
			||||||
| 
						 | 
					@ -80,6 +83,7 @@
 | 
				
			||||||
 * @pdata: SAI block parent data pointer
 | 
					 * @pdata: SAI block parent data pointer
 | 
				
			||||||
 * @np_sync_provider: synchronization provider node
 | 
					 * @np_sync_provider: synchronization provider node
 | 
				
			||||||
 * @sai_ck: kernel clock feeding the SAI clock generator
 | 
					 * @sai_ck: kernel clock feeding the SAI clock generator
 | 
				
			||||||
 | 
					 * @sai_mclk: master clock from SAI mclk provider
 | 
				
			||||||
 * @phys_addr: SAI registers physical base address
 | 
					 * @phys_addr: SAI registers physical base address
 | 
				
			||||||
 * @mclk_rate: SAI block master clock frequency (Hz). set at init
 | 
					 * @mclk_rate: SAI block master clock frequency (Hz). set at init
 | 
				
			||||||
 * @id: SAI sub block id corresponding to sub-block A or B
 | 
					 * @id: SAI sub block id corresponding to sub-block A or B
 | 
				
			||||||
| 
						 | 
					@ -110,6 +114,7 @@ struct stm32_sai_sub_data {
 | 
				
			||||||
	struct stm32_sai_data *pdata;
 | 
						struct stm32_sai_data *pdata;
 | 
				
			||||||
	struct device_node *np_sync_provider;
 | 
						struct device_node *np_sync_provider;
 | 
				
			||||||
	struct clk *sai_ck;
 | 
						struct clk *sai_ck;
 | 
				
			||||||
 | 
						struct clk *sai_mclk;
 | 
				
			||||||
	dma_addr_t phys_addr;
 | 
						dma_addr_t phys_addr;
 | 
				
			||||||
	unsigned int mclk_rate;
 | 
						unsigned int mclk_rate;
 | 
				
			||||||
	unsigned int id;
 | 
						unsigned int id;
 | 
				
			||||||
| 
						 | 
					@ -251,6 +256,177 @@ static const struct snd_kcontrol_new iec958_ctls = {
 | 
				
			||||||
	.put = snd_pcm_iec958_put,
 | 
						.put = snd_pcm_iec958_put,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct stm32_sai_mclk_data {
 | 
				
			||||||
 | 
						struct clk_hw hw;
 | 
				
			||||||
 | 
						unsigned long freq;
 | 
				
			||||||
 | 
						struct stm32_sai_sub_data *sai_data;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw)
 | 
				
			||||||
 | 
					#define STM32_SAI_MAX_CLKS 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai,
 | 
				
			||||||
 | 
									 unsigned long input_rate,
 | 
				
			||||||
 | 
									 unsigned long output_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int version = sai->pdata->conf->version;
 | 
				
			||||||
 | 
						int div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div = DIV_ROUND_CLOSEST(input_rate, output_rate);
 | 
				
			||||||
 | 
						if (div > SAI_XCR1_MCKDIV_MAX(version)) {
 | 
				
			||||||
 | 
							dev_err(&sai->pdev->dev, "Divider %d out of range\n", div);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (input_rate % div)
 | 
				
			||||||
 | 
							dev_dbg(&sai->pdev->dev,
 | 
				
			||||||
 | 
								"Rate not accurate. requested (%ld), actual (%ld)\n",
 | 
				
			||||||
 | 
								output_rate, input_rate / div);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return div;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai,
 | 
				
			||||||
 | 
									 unsigned int div)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int version = sai->pdata->conf->version;
 | 
				
			||||||
 | 
						int ret, cr1, mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (div > SAI_XCR1_MCKDIV_MAX(version)) {
 | 
				
			||||||
 | 
							dev_err(&sai->pdev->dev, "Divider %d out of range\n", div);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
 | 
				
			||||||
 | 
						cr1 = SAI_XCR1_MCKDIV_SET(div);
 | 
				
			||||||
 | 
						ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							dev_err(&sai->pdev->dev, "Failed to update CR1 register\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									      unsigned long *prate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
 | 
				
			||||||
 | 
						struct stm32_sai_sub_data *sai = mclk->sai_data;
 | 
				
			||||||
 | 
						int div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div = stm32_sai_get_clk_div(sai, *prate, rate);
 | 
				
			||||||
 | 
						if (div < 0)
 | 
				
			||||||
 | 
							return div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mclk->freq = *prate / div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mclk->freq;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw,
 | 
				
			||||||
 | 
											unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mclk->freq;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									   unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
 | 
				
			||||||
 | 
						struct stm32_sai_sub_data *sai = mclk->sai_data;
 | 
				
			||||||
 | 
						unsigned int div;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div = stm32_sai_get_clk_div(sai, parent_rate, rate);
 | 
				
			||||||
 | 
						if (div < 0)
 | 
				
			||||||
 | 
							return div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = stm32_sai_set_clk_div(sai, div);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mclk->freq = rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int stm32_sai_mclk_enable(struct clk_hw *hw)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
 | 
				
			||||||
 | 
						struct stm32_sai_sub_data *sai = mclk->sai_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_dbg(&sai->pdev->dev, "Enable master clock\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
 | 
				
			||||||
 | 
									  SAI_XCR1_MCKEN, SAI_XCR1_MCKEN);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void stm32_sai_mclk_disable(struct clk_hw *hw)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
 | 
				
			||||||
 | 
						struct stm32_sai_sub_data *sai = mclk->sai_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_dbg(&sai->pdev->dev, "Disable master clock\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct clk_ops mclk_ops = {
 | 
				
			||||||
 | 
						.enable = stm32_sai_mclk_enable,
 | 
				
			||||||
 | 
						.disable = stm32_sai_mclk_disable,
 | 
				
			||||||
 | 
						.recalc_rate = stm32_sai_mclk_recalc_rate,
 | 
				
			||||||
 | 
						.round_rate = stm32_sai_mclk_round_rate,
 | 
				
			||||||
 | 
						.set_rate = stm32_sai_mclk_set_rate,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk_hw *hw;
 | 
				
			||||||
 | 
						struct stm32_sai_mclk_data *mclk;
 | 
				
			||||||
 | 
						struct device *dev = &sai->pdev->dev;
 | 
				
			||||||
 | 
						const char *pname = __clk_get_name(sai->sai_ck);
 | 
				
			||||||
 | 
						char *mclk_name, *p, *s = (char *)pname;
 | 
				
			||||||
 | 
						int ret, i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mclk = devm_kzalloc(dev, sizeof(mclk), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!mclk)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mclk_name = devm_kcalloc(dev, sizeof(char),
 | 
				
			||||||
 | 
									 SAI_MCLK_NAME_LEN, GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!mclk_name)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Forge mclk clock name from parent clock name and suffix.
 | 
				
			||||||
 | 
						 * String after "_" char is stripped in parent name.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						p = mclk_name;
 | 
				
			||||||
 | 
						while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 6))) {
 | 
				
			||||||
 | 
							*p++ = *s++;
 | 
				
			||||||
 | 
							i++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						STM_SAI_IS_SUB_A(sai) ?
 | 
				
			||||||
 | 
							strncat(p, "a_mclk", 6) : strncat(p, "b_mclk", 6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0);
 | 
				
			||||||
 | 
						mclk->sai_data = sai;
 | 
				
			||||||
 | 
						hw = &mclk->hw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_dbg(dev, "Register master clock %s\n", mclk_name);
 | 
				
			||||||
 | 
						ret = devm_clk_hw_register(&sai->pdev->dev, hw);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							dev_err(dev, "mclk register returned %d\n", ret);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sai->sai_mclk = hw->clk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* register mclk provider */
 | 
				
			||||||
 | 
						return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static irqreturn_t stm32_sai_isr(int irq, void *devid)
 | 
					static irqreturn_t stm32_sai_isr(int irq, void *devid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
 | 
						struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
 | 
				
			||||||
| 
						 | 
					@ -312,15 +488,25 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
 | 
				
			||||||
	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
 | 
						struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
 | 
						if (dir == SND_SOC_CLOCK_OUT) {
 | 
				
			||||||
		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
 | 
							ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
 | 
				
			||||||
					 SAI_XCR1_NODIV,
 | 
										 SAI_XCR1_NODIV,
 | 
				
			||||||
					 (unsigned int)~SAI_XCR1_NODIV);
 | 
										 (unsigned int)~SAI_XCR1_NODIV);
 | 
				
			||||||
		if (ret < 0)
 | 
							if (ret < 0)
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		sai->mclk_rate = freq;
 | 
					 | 
				
			||||||
		dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
 | 
							dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
 | 
				
			||||||
 | 
							sai->mclk_rate = freq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sai->sai_mclk) {
 | 
				
			||||||
 | 
								ret = clk_set_rate_exclusive(sai->sai_mclk,
 | 
				
			||||||
 | 
											     sai->mclk_rate);
 | 
				
			||||||
 | 
								if (ret) {
 | 
				
			||||||
 | 
									dev_err(cpu_dai->dev,
 | 
				
			||||||
 | 
										"Could not set mclk rate\n");
 | 
				
			||||||
 | 
									return ret;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -715,15 +901,9 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
 | 
						struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
 | 
				
			||||||
	int cr1, mask, div = 0;
 | 
						int cr1, mask, div = 0;
 | 
				
			||||||
	int sai_clk_rate, mclk_ratio, den, ret;
 | 
						int sai_clk_rate, mclk_ratio, den;
 | 
				
			||||||
	int version = sai->pdata->conf->version;
 | 
					 | 
				
			||||||
	unsigned int rate = params_rate(params);
 | 
						unsigned int rate = params_rate(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!sai->mclk_rate) {
 | 
					 | 
				
			||||||
		dev_err(cpu_dai->dev, "Mclk rate is null\n");
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!(rate % 11025))
 | 
						if (!(rate % 11025))
 | 
				
			||||||
		clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
 | 
							clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
| 
						 | 
					@ -731,14 +911,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 | 
				
			||||||
	sai_clk_rate = clk_get_rate(sai->sai_ck);
 | 
						sai_clk_rate = clk_get_rate(sai->sai_ck);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (STM_SAI_IS_F4(sai->pdata)) {
 | 
						if (STM_SAI_IS_F4(sai->pdata)) {
 | 
				
			||||||
		/*
 | 
							/* mclk on (NODIV=0)
 | 
				
			||||||
		 *   mclk_rate = 256 * fs
 | 
							 *   mclk_rate = 256 * fs
 | 
				
			||||||
		 *   MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
 | 
							 *   MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
 | 
				
			||||||
		 *   MCKDIV = sai_ck / (2 * mclk_rate) otherwise
 | 
							 *   MCKDIV = sai_ck / (2 * mclk_rate) otherwise
 | 
				
			||||||
 | 
							 * mclk off (NODIV=1)
 | 
				
			||||||
 | 
							 *   MCKDIV ignored. sck = sai_ck
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
 | 
							if (!sai->mclk_rate)
 | 
				
			||||||
			div = DIV_ROUND_CLOSEST(sai_clk_rate,
 | 
								return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (2 * sai_clk_rate >= 3 * sai->mclk_rate) {
 | 
				
			||||||
 | 
								div = stm32_sai_get_clk_div(sai, sai_clk_rate,
 | 
				
			||||||
						    2 * sai->mclk_rate);
 | 
											    2 * sai->mclk_rate);
 | 
				
			||||||
 | 
								if (div < 0)
 | 
				
			||||||
 | 
									return div;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * TDM mode :
 | 
							 * TDM mode :
 | 
				
			||||||
| 
						 | 
					@ -750,8 +938,10 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 | 
				
			||||||
		 * Note: NOMCK/NODIV correspond to same bit.
 | 
							 * Note: NOMCK/NODIV correspond to same bit.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
 | 
							if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
 | 
				
			||||||
			div = DIV_ROUND_CLOSEST(sai_clk_rate,
 | 
								div = stm32_sai_get_clk_div(sai, sai_clk_rate,
 | 
				
			||||||
						(params_rate(params) * 128));
 | 
											    rate * 128);
 | 
				
			||||||
 | 
								if (div < 0)
 | 
				
			||||||
 | 
									return div;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if (sai->mclk_rate) {
 | 
								if (sai->mclk_rate) {
 | 
				
			||||||
				mclk_ratio = sai->mclk_rate / rate;
 | 
									mclk_ratio = sai->mclk_rate / rate;
 | 
				
			||||||
| 
						 | 
					@ -764,31 +954,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 | 
				
			||||||
						mclk_ratio);
 | 
											mclk_ratio);
 | 
				
			||||||
					return -EINVAL;
 | 
										return -EINVAL;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				div = DIV_ROUND_CLOSEST(sai_clk_rate,
 | 
									div = stm32_sai_get_clk_div(sai, sai_clk_rate,
 | 
				
			||||||
							    sai->mclk_rate);
 | 
												    sai->mclk_rate);
 | 
				
			||||||
 | 
									if (div < 0)
 | 
				
			||||||
 | 
										return div;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				/* mclk-fs not set, master clock not active */
 | 
									/* mclk-fs not set, master clock not active */
 | 
				
			||||||
				den = sai->fs_length * params_rate(params);
 | 
									den = sai->fs_length * params_rate(params);
 | 
				
			||||||
				div = DIV_ROUND_CLOSEST(sai_clk_rate, den);
 | 
									div = stm32_sai_get_clk_div(sai, sai_clk_rate,
 | 
				
			||||||
 | 
												    den);
 | 
				
			||||||
 | 
									if (div < 0)
 | 
				
			||||||
 | 
										return div;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (div > SAI_XCR1_MCKDIV_MAX(version)) {
 | 
						return stm32_sai_set_clk_div(sai, div);
 | 
				
			||||||
		dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
 | 
					 | 
				
			||||||
	cr1 = SAI_XCR1_MCKDIV_SET(div);
 | 
					 | 
				
			||||||
	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
 | 
					 | 
				
			||||||
	if (ret < 0) {
 | 
					 | 
				
			||||||
		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
 | 
					 | 
				
			||||||
		return ret;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
 | 
					static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
 | 
				
			||||||
| 
						 | 
					@ -881,6 +1062,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
 | 
				
			||||||
			   SAI_XCR1_NODIV);
 | 
								   SAI_XCR1_NODIV);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clk_disable_unprepare(sai->sai_ck);
 | 
						clk_disable_unprepare(sai->sai_ck);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_rate_exclusive_put(sai->sai_mclk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sai->substream = NULL;
 | 
						sai->substream = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -903,6 +1087,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
 | 
				
			||||||
	struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
 | 
						struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
 | 
				
			||||||
	int cr1 = 0, cr1_mask;
 | 
						int cr1 = 0, cr1_mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sai->cpu_dai = cpu_dai;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
 | 
						sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice,
 | 
						 * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice,
 | 
				
			||||||
| 
						 | 
					@ -1181,6 +1367,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
 | 
				
			||||||
		return PTR_ERR(sai->sai_ck);
 | 
							return PTR_ERR(sai->sai_ck);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (STM_SAI_IS_F4(sai->pdata))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Register mclk provider if requested */
 | 
				
			||||||
 | 
						if (of_find_property(np, "#clock-cells", NULL)) {
 | 
				
			||||||
 | 
							ret = stm32_sai_add_mclk_provider(sai);
 | 
				
			||||||
 | 
							if (ret < 0)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK");
 | 
				
			||||||
 | 
							if (IS_ERR(sai->sai_mclk)) {
 | 
				
			||||||
 | 
								if (PTR_ERR(sai->sai_mclk) != -ENOENT)
 | 
				
			||||||
 | 
									return PTR_ERR(sai->sai_mclk);
 | 
				
			||||||
 | 
								sai->sai_mclk = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue