ASoC: fsl_utils: Add function to handle PLL clock source
authorShengjiu Wang <shengjiu.wang@nxp.com>
Fri, 1 Jul 2022 09:32:36 +0000 (17:32 +0800)
committerMark Brown <broonie@kernel.org>
Tue, 5 Jul 2022 12:00:37 +0000 (13:00 +0100)
i.MX8MQ/MN/MM/MP platforms typically have 2 AUDIO PLLs being
configured to handle 8kHz and 11kHz series audio rates.
Add common function in fsl_utils to handle these two PLL
clock source, which are needed by CPU DAI drivers

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Link: https://lore.kernel.org/r/1656667961-1799-2-git-send-email-shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/fsl/fsl_utils.c
sound/soc/fsl/fsl_utils.h

index 9bab202..b75843e 100644 (file)
@@ -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");
index c5dc2a1..4d5f3d9 100644 (file)
@@ -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 */