[alsa-devel] [PATCH] ASoC: nau8825: extend FLL function
Extend FLL clock source from MCLK/BCLK/FS. Modify FLL calculation and parameter setting.
Signed-off-by: John Hsu KCHSU0@nuvoton.com --- sound/soc/codecs/nau8825.c | 128 ++++++++++++++++++++++++++++++++------------- sound/soc/codecs/nau8825.h | 21 ++++++-- 2 files changed, 108 insertions(+), 41 deletions(-)
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c1b87c5..504c969 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -31,7 +31,7 @@ #include "nau8825.h"
#define NAU_FREF_MAX 13500000 -#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MAX 124000000 #define NAU_FVCO_MIN 90000000
struct nau8825_fll { @@ -874,8 +874,8 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec) static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, struct nau8825_fll *fll_param) { - u64 fvco; - unsigned int fref, i; + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel;
/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. @@ -900,18 +900,23 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, fll_param->ratio = fll_ratio[i].val;
/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. - * FDCO must be within the 90MHz - 100MHz or the FFL cannot be + * FDCO must be within the 90MHz - 124MHz or the FFL cannot be * guaranteed across the full range of operation. * FDCO = freq_out * 2 * mclk_src_scaling */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { fvco = 256 * fs * 2 * mclk_src_scaling[i].param; - if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX) - break; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } } - if (i == ARRAY_SIZE(mclk_src_scaling)) + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) return -EINVAL; - fll_param->mclk_src = mclk_src_scaling[i].val; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional * input based on FDCO, FREF and FLL ratio. @@ -926,7 +931,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, struct nau8825_fll *fll_param) { regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, - NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src); + NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK, + NAU8825_CLK_SRC_MCLK | fll_param->mclk_src); regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, NAU8825_FLL_RATIO_MASK, fll_param->ratio); /* FLL 16-bit fractional input */ @@ -939,10 +945,25 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div); /* select divided VCO input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, - NAU8825_FLL_FILTER_SW_MASK, 0x0000); - /* FLL sigma delta modulator enable */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, - NAU8825_SDM_EN_MASK, NAU8825_SDM_EN); + NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF); + /* Disable free-running mode */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + if (fll_param->fll_frac) { + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_MASK, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_FILTER); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN, NAU8825_SDM_EN); + } else { + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU); + regmap_update_bits(nau8825->regmap, + NAU8825_REG_FLL6, NAU8825_SDM_EN, 0); + } }
/* freq_out must be 256*Fs in order to achieve the best performance */ @@ -970,6 +991,36 @@ static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source, return 0; }
+static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq) +{ + int ret = 0; + + /* We selected MCLK source but the clock itself managed externally */ + if (!nau8825->mclk) + return 0; + + if (!nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + if (nau8825->mclk_freq != freq) { + nau8825->mclk_freq = freq; + + freq = clk_round_rate(nau8825->mclk, freq); + ret = clk_set_rate(nau8825->mclk, freq); + if (ret) { + dev_err(nau8825->dev, "Unable to set mclk rate\n"); + return ret; + } + } + + return 0; +} + static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, unsigned int freq) { @@ -981,29 +1032,9 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); - - /* We selected MCLK source but the clock itself managed externally */ - if (!nau8825->mclk) - break; - - if (!nau8825->mclk_freq) { - ret = clk_prepare_enable(nau8825->mclk); - if (ret) { - dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); - return ret; - } - } - - if (nau8825->mclk_freq != freq) { - nau8825->mclk_freq = freq; - - freq = clk_round_rate(nau8825->mclk, freq); - ret = clk_set_rate(nau8825->mclk, freq); - if (ret) { - dev_err(nau8825->dev, "Unable to set mclk rate\n"); - return ret; - } - } + ret = nau8825_mclk_prepare(nau8825, freq); + if (ret) + return ret;
break; case NAU8825_CLK_INTERNAL: @@ -1011,7 +1042,32 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, NAU8825_DCO_EN); regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + case NAU8825_CLK_FLL_MCLK: + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK); + ret = nau8825_mclk_prepare(nau8825, freq); + if (ret) + return ret; + + break; + case NAU8825_CLK_FLL_BLK: + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK); + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + }
+ break; + case NAU8825_CLK_FLL_FS: + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS); if (nau8825->mclk_freq) { clk_disable_unprepare(nau8825->mclk); nau8825->mclk_freq = 0; diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index dff8edb..8237693 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -112,20 +112,28 @@
/* FLL3 (0x06) */ #define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) +#define NAU8825_FLL_CLK_SRC_SFT 10 +#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
/* FLL4 (0x07) */ #define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
/* FLL5 (0x08) */ -#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14) +#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13) +#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13) +#define NAU8825_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12) +#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12) +#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12)
/* FLL6 (0x9) */ -#define NAU8825_DCO_EN_MASK (0x1 << 15) #define NAU8825_DCO_EN (0x1 << 15) -#define NAU8825_DCO_DIS (0x0 << 15) -#define NAU8825_SDM_EN_MASK (0x1 << 14) #define NAU8825_SDM_EN (0x1 << 14) -#define NAU8825_SDM_DIS (0x0 << 14)
/* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) @@ -306,6 +314,9 @@ enum { NAU8825_CLK_MCLK = 0, NAU8825_CLK_INTERNAL, + NAU8825_CLK_FLL_MCLK, + NAU8825_CLK_FLL_BLK, + NAU8825_CLK_FLL_FS, };
struct nau8825 {
On Mon, Feb 29, 2016 at 02:23:47AM +0800, John Hsu wrote:
Extend FLL clock source from MCLK/BCLK/FS. Modify FLL calculation and parameter setting.
I'm sorry but this really doesn't tell me very much about what this is supposed to do so it's very hard to tell if the change does it or not. It also sounds like there's multiple different changes going on here, if nothing else it sounds like there's the addition of some new sources for the FLL and also some unspecified changes to the calculations. Separate changes should be in separate patches.
- /* We selected MCLK source but the clock itself managed externally */
- if (!nau8825->mclk)
return 0;
That comment sounds *very* suspicous, if we are using MCLK we should manage it via the clock API. If the platform doesn't have good clock support we should fix the platform.
- if (nau8825->mclk_freq != freq) {
nau8825->mclk_freq = freq;
freq = clk_round_rate(nau8825->mclk, freq);
ret = clk_set_rate(nau8825->mclk, freq);
if (ret) {
dev_err(nau8825->dev, "Unable to set mclk rate\n");
return ret;
}
We store the frequency even if we failed to set it. This means that if we retry we'll skip over setting which is buggy.
Hi,
On 3/1/2016 11:23 AM, Mark Brown wrote:
On Mon, Feb 29, 2016 at 02:23:47AM +0800, John Hsu wrote:
Extend FLL clock source from MCLK/BCLK/FS. Modify FLL calculation and parameter setting.
I'm sorry but this really doesn't tell me very much about what this is supposed to do so it's very hard to tell if the change does it or not. It also sounds like there's multiple different changes going on here, if nothing else it sounds like there's the addition of some new sources for the FLL and also some unspecified changes to the calculations. Separate changes should be in separate patches.
In the patch, we add FLL clock source selection. The source can be from MCLK, BCLK or FS. Besides, driver extend higher frequency for better performance in FLL calculation, and has different register apply if fraction or not. Just separate it. Right?
- /* We selected MCLK source but the clock itself managed externally */
- if (!nau8825->mclk)
return 0;
That comment sounds *very* suspicous, if we are using MCLK we should manage it via the clock API. If the platform doesn't have good clock support we should fix the platform.
In initiation, we get mclk object from platform as the following code. If the mclk is not found, we don't need to prepare it in the driver.
nau8825->mclk = devm_clk_get(dev, "mclk"); ... } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { /* The MCLK is managed externally or not used at all */ nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
- if (nau8825->mclk_freq != freq) {
nau8825->mclk_freq = freq;
freq = clk_round_rate(nau8825->mclk, freq);
ret = clk_set_rate(nau8825->mclk, freq);
if (ret) {
dev_err(nau8825->dev, "Unable to set mclk rate\n");
return ret;
}
We store the frequency even if we failed to set it. This means that if we retry we'll skip over setting which is buggy.
Yes, it's better to store it when everything get done correctly.
On Tue, Mar 08, 2016 at 07:58:53PM +0800, John Hsu wrote:
In the patch, we add FLL clock source selection. The source can be from MCLK, BCLK or FS. Besides, driver extend higher frequency for better performance in FLL calculation, and has different register apply if fraction or not. Just separate it. Right?
I think that's what I was asking for, yes.
That comment sounds *very* suspicous, if we are using MCLK we should manage it via the clock API. If the platform doesn't have good clock support we should fix the platform.
In initiation, we get mclk object from platform as the following code. If the mclk is not found, we don't need to prepare it in the driver.
nau8825->mclk = devm_clk_get(dev, "mclk"); ... } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { /* The MCLK is managed externally or not used at all */ nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
This really isn't a good way to be handling things, you should be ensuring that platforms that have an MCLK provide one via the clock API. If the clock is missing that should indicate that it's the second case where it's not used at all.
On 3/9/2016 10:40 AM, Mark Brown wrote:
On Tue, Mar 08, 2016 at 07:58:53PM +0800, John Hsu wrote:
In the patch, we add FLL clock source selection. The source can be from MCLK, BCLK or FS. Besides, driver extend higher frequency for better performance in FLL calculation, and has different register apply if fraction or not. Just separate it. Right?
I think that's what I was asking for, yes.
I'll split these solution into two patches later and resubmit.
That comment sounds *very* suspicous, if we are using MCLK we should manage it via the clock API. If the platform doesn't have good clock support we should fix the platform.
In initiation, we get mclk object from platform as the following code. If the mclk is not found, we don't need to prepare it in the driver.
nau8825->mclk = devm_clk_get(dev, "mclk"); ... } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { /* The MCLK is managed externally or not used at all */ nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
This really isn't a good way to be handling things, you should be ensuring that platforms that have an MCLK provide one via the clock API. If the clock is missing that should indicate that it's the second case where it's not used at all.
Do you mean I should use devm_clk_get to check mclk exist instead of nau8825->mclk in nau8825_mclk_prepare function? And return a warn- ing message if mclk is missing?
On Thu, Mar 10, 2016 at 12:31:54PM +0800, John Hsu wrote:
On 3/9/2016 10:40 AM, Mark Brown wrote:
This really isn't a good way to be handling things, you should be ensuring that platforms that have an MCLK provide one via the clock API. If the clock is missing that should indicate that it's the second case where it's not used at all.
Do you mean I should use devm_clk_get to check mclk exist instead of nau8825->mclk in nau8825_mclk_prepare function? And return a warn- ing message if mclk is missing?
That might make sense, yes. Let's see the code.
participants (2)
-
John Hsu
-
Mark Brown