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: Olivier Moysan <olivier.moysan@st.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
d6ba3f815b
Коммит
e37c2deafe
|
@ -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;
|
||||
|
||||
/* If master clock is used, set parent clock now */
|
||||
ret = stm32_sai_set_parent_clock(sai, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@ -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_rate_exclusive_put(sai->sai_mclk);
|
||||
clk_disable_unprepare(sai->sai_ck);
|
||||
|
||||
spin_lock_irqsave(&sai->irq_lock, flags);
|
||||
sai->substream = NULL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче