[PATCH 2/2] ASoC: fsl_sai: Export transmitter MCLK as clock

Shengjiu Wang shengjiu.wang at gmail.com
Mon Dec 19 10:18:19 CET 2022


On Sun, Dec 11, 2022 at 10:41 AM Marek Vasut <marex at denx.de> wrote:

> For SAI to generate MCLK on external SoC pad, the transmitter must be
> enabled as well. With transmitter disabled, no clock are generated.
> Enable the transmitter using the TERE bit.
>
> Rather than doing ad-hoc enablement of the TERE bit, convert the mclk
> handling into a clock provider and adjust the SAI driver points which
> modify the TERE bit to instead enable and disable the clock. Register
> two clock, mclk_tx and mclk_rx, each representing TCSR and RCSR TERE
> bit respectively. Expose only the mclk_tx, since it does not really
> matter which TERE bit is enabled for the generation of output MCLK,
> but it does matter which TERE bit is enabled for internal operation
> of the SAI.
>
> As a nice bonus, this simplifies the MCLK handling in the driver itself.
>
> Signed-off-by: Marek Vasut <marex at denx.de>
> ---
> Cc: Fabio Estevam <festevam at gmail.com>
> Cc: Jaroslav Kysela <perex at perex.cz>
> Cc: Liam Girdwood <lgirdwood at gmail.com>
> Cc: Mark Brown <broonie at kernel.org>
> Cc: Nicolin Chen <nicoleotsuka at gmail.com>
> Cc: Shengjiu Wang <shengjiu.wang at gmail.com>
> Cc: Takashi Iwai <tiwai at suse.com>
> Cc: Xiubo Li <Xiubo.Lee at gmail.com>
> To: alsa-devel at alsa-project.org
> ---
>  sound/soc/fsl/fsl_sai.c | 226 ++++++++++++++++++++++++++++++----------
>  sound/soc/fsl/fsl_sai.h |   3 +
>  2 files changed, 176 insertions(+), 53 deletions(-)
>
> diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
> index ade5a6b44b66a..944450ba19b62 100644
> --- a/sound/soc/fsl/fsl_sai.c
> +++ b/sound/soc/fsl/fsl_sai.c
> @@ -185,34 +185,9 @@ static int fsl_sai_set_dai_bclk_ratio(struct
> snd_soc_dai *dai,
>         return 0;
>  }
>
> -static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
> -               int clk_id, unsigned int freq, bool tx)
> +static int fsl_sai_set_dai_sysclk_tr(struct fsl_sai *sai, int clk_id,
> bool tx)
>  {
> -       struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
> -       unsigned int ofs = sai->soc_data->reg_offset;
> -       u32 val_cr2 = 0;
> -
> -       switch (clk_id) {
> -       case FSL_SAI_CLK_BUS:
> -               val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
> -               break;
> -       case FSL_SAI_CLK_MAST1:
> -               val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
> -               break;
> -       case FSL_SAI_CLK_MAST2:
> -               val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
> -               break;
> -       case FSL_SAI_CLK_MAST3:
> -               val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
> -               break;
> -       default:
> -               return -EINVAL;
> -       }
> -
> -       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
> -                          FSL_SAI_CR2_MSEL_MASK, val_cr2);
> -
> -       return 0;
> +       return clk_set_parent(sai->mclk_clk_out[tx],
> sai->mclk_clk[clk_id]);
>  }
>
>  static int fsl_sai_set_mclk_rate(struct snd_soc_dai *dai, int clk_id,
> unsigned int freq)
> @@ -257,13 +232,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai
> *cpu_dai,
>                 }
>         }
>
> -       ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
> +       ret = fsl_sai_set_dai_sysclk_tr(sai, clk_id, true);
>         if (ret) {
>                 dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
>                 return ret;
>         }
>
> -       ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, false);
> +       ret = fsl_sai_set_dai_sysclk_tr(sai, clk_id, false);
>         if (ret)
>                 dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
>
> @@ -487,8 +462,7 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai,
> bool tx, u32 freq)
>         else
>                 return 0;
>
> -       regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK,
> -                          FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
> +       fsl_sai_set_dai_sysclk_tr(sai, sai->mclk_id[tx], tx);
>
>         if (savediv == 1)
>                 regmap_update_bits(sai->regmap, reg,
> @@ -707,32 +681,41 @@ static int fsl_sai_hw_free(struct snd_pcm_substream
> *substream,
>  static void fsl_sai_reset(struct fsl_sai *sai, bool rx, bool tx)
>  {
>         unsigned int ofs = sai->soc_data->reg_offset;
> +       bool tx_keep_tere = false, rx_keep_tere = false;
> +       u32 tcsr = 0, rcsr = 0;
> +
> +       if (sai->mclk_clk_out[0])
> +               rx_keep_tere = clk_hw_is_enabled(&(sai->mclk_clk_hw[0]));
> +       if (sai->mclk_clk_out[1])
> +               tx_keep_tere = clk_hw_is_enabled(&(sai->mclk_clk_hw[1]));
> +
> +       if (rx_keep_tere) {
> +               regmap_read(sai->regmap, FSL_SAI_RCSR(ofs), &rcsr);
> +               rcsr &= FSL_SAI_CSR_TERE;
> +       }
> +
> +       if (tx_keep_tere) {
> +               regmap_read(sai->regmap, FSL_SAI_TCSR(ofs), &tcsr);
> +               tcsr &= FSL_SAI_CSR_TERE;
> +       }
>
>         if (tx)
> -               regmap_write(sai->regmap, FSL_SAI_TCSR(ofs),
> FSL_SAI_CSR_SR);
> +               regmap_write(sai->regmap, FSL_SAI_TCSR(ofs),
> FSL_SAI_CSR_SR | tcsr);
>         if (rx)
> -               regmap_write(sai->regmap, FSL_SAI_RCSR(ofs),
> FSL_SAI_CSR_SR);
> +               regmap_write(sai->regmap, FSL_SAI_RCSR(ofs),
> FSL_SAI_CSR_SR | rcsr);
>         usleep_range(1000, 2000);
>

Not sure if you have test your patch.
there is kernel dump for "scheduling while atomic"

[  248.778590] BUG: scheduling while atomic: swapper/0/0/0x00010003
[  248.784618] Modules linked in:
[  248.787677] CPU: 0 PID: 0 Comm: swapper/0 Not tainted
6.1.0-rc6-00014-gf2b129b4c056-dirty #136
[  248.796292] Hardware name: NXP i.MX8MNano EVK board (DT)
[  248.801604] Call trace:
[  248.804051]  dump_backtrace.part.0+0xdc/0xf0
[  248.808337]  show_stack+0x18/0x40
[  248.811660]  dump_stack_lvl+0x64/0x80
[  248.815327]  dump_stack+0x18/0x34
[  248.818645]  __schedule_bug+0x60/0x80
[  248.822311]  __schedule+0x630/0x710
[  248.825808]  schedule+0x5c/0xd0
[  248.828955]  schedule_hrtimeout_range_clock+0xf4/0x134
[  248.834096]  schedule_hrtimeout_range+0x14/0x20
[  248.838629]  usleep_range_state+0x74/0xb0
[  248.842641]  fsl_sai_reset+0x13c/0x1cc
[  248.846397]  fsl_sai_config_disable+0x94/0xb0
[  248.850760]  fsl_sai_trigger+0x208/0x29c
[  248.854688]  snd_soc_pcm_dai_trigger+0xcc/0x26c
[  248.859223]  soc_pcm_trigger+0x164/0x1f4
[  248.863154]  snd_pcm_do_stop+0x68/0x90



>         if (tx)
> -               regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
> +               regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), tcsr);
>         if (rx)
> -               regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
> +               regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), rcsr);
>  }
>
>  static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
>  {
>         unsigned int ofs = sai->soc_data->reg_offset;
>         bool tx = dir == TX;
> -       u32 xcsr, count = 100;
>
> -       regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
> -                          FSL_SAI_CSR_TERE, 0);
> -
> -       /* TERE will remain set till the end of current frame */
> -       do {
> -               udelay(10);
> -               regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
> -       } while (--count && xcsr & FSL_SAI_CSR_TERE);
> +       clk_disable_unprepare(sai->mclk_clk_out[tx]);
>
>         regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
>                            FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
> @@ -780,8 +763,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream
> *substream, int cmd,
>                 regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
>                                    FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
>
> -               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
> -                                  FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
> +               clk_prepare_enable(sai->mclk_clk_out[tx]);
>                 /*
>                  * Enable the opposite direction for synchronous mode
>                  * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for
> Tx
> @@ -794,8 +776,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream
> *substream, int cmd,
>                  * be safe to do, judging by years of testing results.
>                  */
>                 if (fsl_sai_dir_is_synced(sai, adir))
> -                       regmap_update_bits(sai->regmap,
> FSL_SAI_xCSR((!tx), ofs),
> -                                          FSL_SAI_CSR_TERE,
> FSL_SAI_CSR_TERE);
> +                       clk_prepare_enable(sai->mclk_clk_out[!tx]);
>
>                 regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
>                                    FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
> @@ -1281,6 +1262,86 @@ static int fsl_sai_read_dlcfg(struct fsl_sai *sai)
>         return 0;
>  }
>
> +static struct fsl_sai *mclk_get_clk_hw_to_sai(struct clk_hw *hw, bool *tx)
> +{
> +       *tx = !strcmp(clk_hw_get_name(hw), "mclk_tx");
> +
> +       return container_of(hw, struct fsl_sai, mclk_clk_hw[*tx]);
> +}
> +
> +static int mclk_gate_enable(struct clk_hw *hw)
> +{
> +       bool tx;
> +       struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
> +       unsigned int ofs = sai->soc_data->reg_offset;
> +
> +       pm_runtime_get_sync(&sai->pdev->dev);
> +
> +       /* Transmitter must be enabled to generate MCLK on pad */
> +       regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
> +                          FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
> +
> +       return 0;
> +}
> +
> +static void mclk_gate_disable(struct clk_hw *hw)
> +{
> +       bool tx;
> +       struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
> +       unsigned int ofs = sai->soc_data->reg_offset;
> +       u32 xcsr, count = 100;
> +
> +       regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs),
> FSL_SAI_CSR_TERE, 0);
> +
> +       /* TERE will remain set till the end of current frame */
> +       do {
> +               udelay(10);
> +               regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
> +       } while (--count && xcsr & FSL_SAI_CSR_TERE);
> +
> +       pm_runtime_put_sync(&sai->pdev->dev);
> +}
> +
> +static int mclk_gate_is_enabled(struct clk_hw *hw)
> +{
> +       bool tx;
> +       struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
> +       unsigned int ofs = sai->soc_data->reg_offset;
> +       u32 xcsr;
> +
> +       regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr);
> +
> +       return !!(xcsr & FSL_SAI_CSR_TERE);
> +}
> +
> +static u8 mclk_gate_get_parent(struct clk_hw *hw)
> +{
> +       bool tx;
> +       struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
> +
> +       return sai->mclk_id[tx];
> +}
> +
> +static int mclk_gate_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       bool tx;
> +       struct fsl_sai *sai = mclk_get_clk_hw_to_sai(hw, &tx);
> +       unsigned int ofs = sai->soc_data->reg_offset;
> +
> +       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
> +                          FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(index));
> +
> +       return 0;
> +}
> +
> +const struct clk_ops mclk_gate_ops = {
> +       .enable         = mclk_gate_enable,
> +       .disable        = mclk_gate_disable,
> +       .is_enabled     = mclk_gate_is_enabled,
> +       .get_parent     = mclk_gate_get_parent,
> +       .set_parent     = mclk_gate_set_parent,
> +};
> +
>  static int fsl_sai_runtime_suspend(struct device *dev);
>  static int fsl_sai_runtime_resume(struct device *dev);
>
> @@ -1295,6 +1356,13 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>         int irq, ret, i;
>         int index;
>         u32 dmas[4];
> +       const char *mclk_parent_names[FSL_SAI_MCLK_MAX];
> +       const char *mclk_rxtx_names[2] = { "mclk_rx", "mclk_tx" };
>

please consider multi sai instance case, this name will duplicate
that cause sai probe issue.

best regards
wang shengjiu

+       struct clk_init_data mclk_init = {
> +               .ops            = &mclk_gate_ops,
> +               .parent_names   = mclk_parent_names,
> +               .num_parents    = FSL_SAI_MCLK_MAX,
> +       };
>
>         sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
>         if (!sai)
> @@ -1341,12 +1409,14 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>                                         i, PTR_ERR(sai->mclk_clk[i]));
>                         sai->mclk_clk[i] = NULL;
>                 }
> +               mclk_parent_names[i] = __clk_get_name(sai->mclk_clk[i]);
>         }
>
>         if (sai->soc_data->mclk0_is_mclk1)
>                 sai->mclk_clk[0] = sai->mclk_clk[1];
>         else
>                 sai->mclk_clk[0] = sai->bus_clk;
> +       mclk_parent_names[0] = __clk_get_name(sai->mclk_clk[0]);
>
>         fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
>                                 &sai->pll11k_clk);
> @@ -1443,8 +1513,34 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>
>         /* Get sai version */
>         ret = fsl_sai_check_version(dev);
> -       if (ret < 0)
> +       if (ret < 0) {
>                 dev_warn(dev, "Error reading SAI version: %d\n", ret);
> +               goto err_pm_get_sync;
> +       }
> +
> +       mclk_init.name = mclk_rxtx_names[0];
> +       sai->mclk_clk_hw[0].init = &mclk_init;
> +       ret = of_clk_hw_register(np, &sai->mclk_clk_hw[0]);
> +       if (ret)
> +               goto err_pm_get_sync;
> +
> +       mclk_init.name = mclk_rxtx_names[1];
> +       sai->mclk_clk_hw[1].init = &mclk_init;
> +       ret = of_clk_hw_register(np, &sai->mclk_clk_hw[1]);
> +       if (ret)
> +               goto err_clk_unregister_rx;
> +
> +       sai->mclk_clk_out[0] = clk_hw_get_clk(&sai->mclk_clk_hw[0], NULL);
> +       if (IS_ERR(sai->mclk_clk_out[0])) {
> +               ret = PTR_ERR(sai->mclk_clk_out[0]);
> +               goto err_clk_unregister_tx;
> +       }
> +
> +       sai->mclk_clk_out[1] = clk_hw_get_clk(&sai->mclk_clk_hw[1], NULL);
> +       if (IS_ERR(sai->mclk_clk_out[1])) {
> +               ret = PTR_ERR(sai->mclk_clk_out[1]);
> +               goto err_clk_put_rx;
> +       }
>
>         /* Select MCLK direction */
>         if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
> @@ -1455,7 +1551,7 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>
>         ret = pm_runtime_put_sync(dev);
>         if (ret < 0 && ret != -ENOSYS)
> -               goto err_pm_get_sync;
> +               goto err_clk_put_tx;
>
>         /*
>          * Register platform component before registering cpu dai for there
> @@ -1466,21 +1562,39 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>                 if (ret) {
>                         if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
>                                 dev_err(dev, "Error: You must enable the
> imx-pcm-dma support!\n");
> -                       goto err_pm_get_sync;
> +                       goto err_clk_put_tx;
>                 }
>         } else {
>                 ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
>                 if (ret)
> -                       goto err_pm_get_sync;
> +                       goto err_clk_put_tx;
>         }
>
>         ret = devm_snd_soc_register_component(dev, &fsl_component,
>                                               &sai->cpu_dai_drv, 1);
>         if (ret)
> -               goto err_pm_get_sync;
> +               goto err_clk_put_tx;
> +
> +       /* Expose only the TX clock */
> +       ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
> +                                         &sai->mclk_clk_hw[1]);
> +       if (ret)
> +               goto err_clk_put_tx;
>
>         return ret;
>
> +err_clk_put_tx:
> +       clk_put(sai->mclk_clk_out[1]);
> +
> +err_clk_put_rx:
> +       clk_put(sai->mclk_clk_out[0]);
> +
> +err_clk_unregister_tx:
> +       clk_hw_unregister(&sai->mclk_clk_hw[1]);
> +
> +err_clk_unregister_rx:
> +       clk_hw_unregister(&sai->mclk_clk_hw[0]);
> +
>  err_pm_get_sync:
>         if (!pm_runtime_status_suspended(dev))
>                 fsl_sai_runtime_suspend(dev);
> @@ -1492,6 +1606,12 @@ static int fsl_sai_probe(struct platform_device
> *pdev)
>
>  static int fsl_sai_remove(struct platform_device *pdev)
>  {
> +       struct fsl_sai *sai = dev_get_drvdata(&pdev->dev);
> +
> +       clk_put(sai->mclk_clk_out[1]);
> +       clk_put(sai->mclk_clk_out[0]);
> +       clk_hw_unregister(&sai->mclk_clk_hw[1]);
> +       clk_hw_unregister(&sai->mclk_clk_hw[0]);
>         pm_runtime_disable(&pdev->dev);
>         if (!pm_runtime_status_suspended(&pdev->dev))
>                 fsl_sai_runtime_suspend(&pdev->dev);
> diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
> index 197748a888d5f..f2abf2d6de218 100644
> --- a/sound/soc/fsl/fsl_sai.h
> +++ b/sound/soc/fsl/fsl_sai.h
> @@ -6,6 +6,7 @@
>  #ifndef __FSL_SAI_H
>  #define __FSL_SAI_H
>
> +#include <linux/clk-provider.h>
>  #include <linux/dma/imx-dma.h>
>  #include <sound/dmaengine_pcm.h>
>
> @@ -277,6 +278,8 @@ struct fsl_sai {
>         struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
>         struct clk *pll8k_clk;
>         struct clk *pll11k_clk;
> +       struct clk_hw mclk_clk_hw[2];   /* MCLK provider */
> +       struct clk *mclk_clk_out[2];    /* MCLK output */
>         struct resource *res;
>
>         bool is_consumer_mode;
> --
> 2.35.1
>
>


More information about the Alsa-devel mailing list