Unverified Commit e37c2dea authored by Olivier Moysan's avatar Olivier Moysan Committed by Mark Brown
Browse files

ASoC: stm32: sai: fix master clock management



When master clock is used, master clock rate is set exclusively.
Parent clocks of master clock cannot be changed after a call to
clk_set_rate_exclusive(). So the parent clock of SAI kernel clock
must be set before.
Ensure also that exclusive rate operations are balanced
in STM32 SAI driver.

Signed-off-by: default avatarOlivier Moysan <olivier.moysan@st.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent d6ba3f81
Loading
Loading
Loading
Loading
+47 −17
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@
#define SAI_IEC60958_STATUS_BYTES	24

#define SAI_MCLK_NAME_LEN		32
#define SAI_RATE_11K			11025

/**
 * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
@@ -309,6 +310,25 @@ static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai,
	return ret;
}

static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai,
				      unsigned int rate)
{
	struct platform_device *pdev = sai->pdev;
	struct clk *parent_clk = sai->pdata->clk_x8k;
	int ret;

	if (!(rate % SAI_RATE_11K))
		parent_clk = sai->pdata->clk_x11k;

	ret = clk_set_parent(sai->sai_ck, parent_clk);
	if (ret)
		dev_err(&pdev->dev, " Error %d setting sai_ck parent clock. %s",
			ret, ret == -EBUSY ?
			"Active stream rates conflict\n" : "\n");

	return ret;
}

static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
				      unsigned long *prate)
{
@@ -490,25 +510,29 @@ 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);
	int ret;

	if (dir == SND_SOC_CLOCK_OUT) {
	if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) {
		ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
					 SAI_XCR1_NODIV,
					 (unsigned int)~SAI_XCR1_NODIV);
		if (ret < 0)
			return ret;

		dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
		sai->mclk_rate = freq;
		/* If master clock is used, set parent clock now */
		ret = stm32_sai_set_parent_clock(sai, freq);
		if (ret)
			return ret;

		if (sai->sai_mclk) {
			ret = clk_set_rate_exclusive(sai->sai_mclk,
						     sai->mclk_rate);
		ret = clk_set_rate_exclusive(sai->sai_mclk, freq);
		if (ret) {
			dev_err(cpu_dai->dev,
				ret == -EBUSY ?
				"Active streams have incompatible rates" :
				"Could not set mclk rate\n");
			return ret;
		}
		}

		dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
		sai->mclk_rate = freq;
	}

	return 0;
@@ -916,11 +940,13 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
	int div = 0, cr1 = 0;
	int sai_clk_rate, mclk_ratio, den;
	unsigned int rate = params_rate(params);
	int ret;

	if (!(rate % 11025))
		clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
	else
		clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
	if (!sai->sai_mclk) {
		ret = stm32_sai_set_parent_clock(sai, rate);
		if (ret)
			return ret;
	}
	sai_clk_rate = clk_get_rate(sai->sai_ck);

	if (STM_SAI_IS_F4(sai->pdata)) {
@@ -1079,9 +1105,13 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
	regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV,
			   SAI_XCR1_NODIV);

	clk_disable_unprepare(sai->sai_ck);

	/* Release mclk rate only if rate was actually set */
	if (sai->mclk_rate) {
		clk_rate_exclusive_put(sai->sai_mclk);
		sai->mclk_rate = 0;
	}

	clk_disable_unprepare(sai->sai_ck);

	spin_lock_irqsave(&sai->irq_lock, flags);
	sai->substream = NULL;