diff options
Diffstat (limited to 'sound/soc/fsl/fsl_utils.c')
-rw-r--r-- | sound/soc/fsl/fsl_utils.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 9bab202569af..d69a6b9795bf 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -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,118 @@ 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 = NULL, *npll = NULL; + bool reparent = false; + int ret; + + 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:%d\n", ret); + } +} +EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); + +/** + * fsl_asoc_constrain_rates - constrain rates according to clocks + * + * @target_constr: target constraint + * @original_constr: original constraint + * @pll8k_clk: PLL clock pointer for 8kHz + * @pll11k_clk: PLL clock pointer for 11kHz + * @ext_clk: External clock pointer + * @target_rates: target rates array + * + * This function constrain rates according to clocks + */ +void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, + const struct snd_pcm_hw_constraint_list *original_constr, + struct clk *pll8k_clk, struct clk *pll11k_clk, + struct clk *ext_clk, int *target_rates) +{ + int i, j, k = 0; + u64 clk_rate[3]; + + *target_constr = *original_constr; + if (pll8k_clk || pll11k_clk || ext_clk) { + target_constr->list = target_rates; + target_constr->count = 0; + for (i = 0; i < original_constr->count; i++) { + clk_rate[0] = clk_get_rate(pll8k_clk); + clk_rate[1] = clk_get_rate(pll11k_clk); + clk_rate[2] = clk_get_rate(ext_clk); + for (j = 0; j < 3; j++) { + if (clk_rate[j] != 0 && + do_div(clk_rate[j], original_constr->list[i]) == 0) { + target_rates[k++] = original_constr->list[i]; + target_constr->count++; + break; + } + } + } + + /* protection for if there is no proper rate found*/ + if (!target_constr->count) + *target_constr = *original_constr; + } +} +EXPORT_SYMBOL(fsl_asoc_constrain_rates); + MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_LICENSE("GPL v2"); |