From da28ed585b26dc6eb0c8d897a9b842a86dd6a659 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:17 +0000 Subject: ASoC: arizona: An OUTDIV of 1 is not valid, avoid this One is not a valid value for the OUTDIV start searching at 2 instead. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index e4295fe..d908046 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1406,7 +1406,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, Fref /= div; /* Fvco should be over the targt; don't check the upper bound */ - div = 1; + div = 2; while (Fout * div < 90000000 * fll->vco_mult) { div++; if (div > 7) { -- cgit v1.1 From 87383ac5a73ff34c60d3ea483bf24cabb27fb522 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:18 +0000 Subject: ASoC: arizona: Add defines for FLL configuration constants Improve readability by adding defines for some of the constants associated with FLL configuration. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index d908046..3d4408d 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -53,6 +53,12 @@ #define ARIZONA_AIF_RX_ENABLES 0x1A #define ARIZONA_AIF_FORCE_WRITE 0x1B +#define ARIZONA_FLL_MAX_FREF 13500000 +#define ARIZONA_FLL_MIN_FVCO 90000000 +#define ARIZONA_FLL_MAX_REFDIV 8 +#define ARIZONA_FLL_MIN_OUTDIV 2 +#define ARIZONA_FLL_MAX_OUTDIV 7 + #define arizona_fll_err(_fll, fmt, ...) \ dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_warn(_fll, fmt, ...) \ @@ -1390,11 +1396,11 @@ static int arizona_calc_fll(struct arizona_fll *fll, /* Fref must be <=13.5MHz */ div = 1; cfg->refdiv = 0; - while ((Fref / div) > 13500000) { + while ((Fref / div) > ARIZONA_FLL_MAX_FREF) { div *= 2; cfg->refdiv++; - if (div > 8) { + if (div > ARIZONA_FLL_MAX_REFDIV) { arizona_fll_err(fll, "Can't scale %dMHz in to <=13.5MHz\n", Fref); @@ -1406,10 +1412,10 @@ static int arizona_calc_fll(struct arizona_fll *fll, Fref /= div; /* Fvco should be over the targt; don't check the upper bound */ - div = 2; - while (Fout * div < 90000000 * fll->vco_mult) { + div = ARIZONA_FLL_MIN_OUTDIV; + while (Fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { div++; - if (div > 7) { + if (div > ARIZONA_FLL_MAX_OUTDIV) { arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", Fout); return -EINVAL; -- cgit v1.1 From 61719db8141acde1a6293bbbddc733655defcc3c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:19 +0000 Subject: ASoC: arizona: Move set of OUTDIV in to arizona_apply_fll Since we know in arizona_apply_fll if we are setting the sync or ref path there is no need to set the outdiv seperately anymore. This patch moves this from arizona_enable_fll to arizona_apply_fll. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 3d4408d..9afd8c4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1502,14 +1502,18 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base, cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); - if (sync) - regmap_update_bits_async(arizona->regmap, base + 0x7, - ARIZONA_FLL1_GAIN_MASK, - cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); - else - regmap_update_bits_async(arizona->regmap, base + 0x9, - ARIZONA_FLL1_GAIN_MASK, - cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + if (sync) { + regmap_update_bits(arizona->regmap, base + 0x7, + ARIZONA_FLL1_GAIN_MASK, + cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + } else { + regmap_update_bits(arizona->regmap, base + 0x5, + ARIZONA_FLL1_OUTDIV_MASK, + cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); + regmap_update_bits(arizona->regmap, base + 0x9, + ARIZONA_FLL1_GAIN_MASK, + cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + } regmap_update_bits_async(arizona->regmap, base + 2, ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, @@ -1546,10 +1550,6 @@ static void arizona_enable_fll(struct arizona_fll *fll, */ if (fll->ref_src >= 0 && fll->ref_freq && fll->ref_src != fll->sync_src) { - regmap_update_bits_async(arizona->regmap, fll->base + 5, - ARIZONA_FLL1_OUTDIV_MASK, - ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); - arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, false); if (fll->sync_src >= 0) { @@ -1558,10 +1558,6 @@ static void arizona_enable_fll(struct arizona_fll *fll, use_sync = true; } } else if (fll->sync_src >= 0) { - regmap_update_bits_async(arizona->regmap, fll->base + 5, - ARIZONA_FLL1_OUTDIV_MASK, - sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); - arizona_apply_fll(arizona, fll->base, sync, fll->sync_src, false); -- cgit v1.1 From 23f785a8bc33a98c4c384a653b9bff9c0cc3591d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:20 +0000 Subject: ASoC: arizona: Move calculation of FLL configuration Currently the FLL configuration is calculated before it is known which FLL path the configuration will be applied to. Newer versions of the IP have differences in the configuration required for each FLL path, which makes it complicated to calculate the FLL configuration in advance. This patch simply checks the validity of a requested input and output frequency before we know which FLL path they will be applied to and saves the actual calculation of the configuration until we know where the settings will be applied. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 79 ++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 35 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 9afd8c4..7398c69 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1383,6 +1383,29 @@ struct arizona_fll_cfg { int gain; }; +static int arizona_validate_fll(struct arizona_fll *fll, + unsigned int Fref, + unsigned int Fout) +{ + unsigned int Fvco_min; + + if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) { + arizona_fll_err(fll, + "Can't scale %dMHz in to <=13.5MHz\n", + Fref); + return -EINVAL; + } + + Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult; + if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) { + arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + + return 0; +} + static int arizona_calc_fll(struct arizona_fll *fll, struct arizona_fll_cfg *cfg, unsigned int Fref, @@ -1400,12 +1423,8 @@ static int arizona_calc_fll(struct arizona_fll *fll, div *= 2; cfg->refdiv++; - if (div > ARIZONA_FLL_MAX_REFDIV) { - arizona_fll_err(fll, - "Can't scale %dMHz in to <=13.5MHz\n", - Fref); + if (div > ARIZONA_FLL_MAX_REFDIV) return -EINVAL; - } } /* Apply the division for our remaining calculations */ @@ -1415,11 +1434,8 @@ static int arizona_calc_fll(struct arizona_fll *fll, div = ARIZONA_FLL_MIN_OUTDIV; while (Fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { div++; - if (div > ARIZONA_FLL_MAX_OUTDIV) { - arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", - Fout); + if (div > ARIZONA_FLL_MAX_OUTDIV) return -EINVAL; - } } target = Fout * div / fll->vco_mult; cfg->outdiv = div; @@ -1536,13 +1552,12 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll) return reg & ARIZONA_FLL1_ENA; } -static void arizona_enable_fll(struct arizona_fll *fll, - struct arizona_fll_cfg *ref, - struct arizona_fll_cfg *sync) +static void arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; int ret; bool use_sync = false; + struct arizona_fll_cfg cfg; /* * If we have both REFCLK and SYNCCLK then enable both, @@ -1550,15 +1565,21 @@ static void arizona_enable_fll(struct arizona_fll *fll, */ if (fll->ref_src >= 0 && fll->ref_freq && fll->ref_src != fll->sync_src) { - arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, + arizona_calc_fll(fll, &cfg, fll->ref_freq, fll->fout); + + arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, false); if (fll->sync_src >= 0) { - arizona_apply_fll(arizona, fll->base + 0x10, sync, + arizona_calc_fll(fll, &cfg, fll->sync_freq, fll->fout); + + arizona_apply_fll(arizona, fll->base + 0x10, &cfg, fll->sync_src, true); use_sync = true; } } else if (fll->sync_src >= 0) { - arizona_apply_fll(arizona, fll->base, sync, + arizona_calc_fll(fll, &cfg, fll->sync_freq, fll->fout); + + arizona_apply_fll(arizona, fll->base, &cfg, fll->sync_src, false); regmap_update_bits_async(arizona->regmap, fll->base + 0x11, @@ -1620,32 +1641,22 @@ static void arizona_disable_fll(struct arizona_fll *fll) int arizona_set_fll_refclk(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - struct arizona_fll_cfg ref, sync; int ret; if (fll->ref_src == source && fll->ref_freq == Fref) return 0; - if (fll->fout) { - if (Fref > 0) { - ret = arizona_calc_fll(fll, &ref, Fref, fll->fout); - if (ret != 0) - return ret; - } - - if (fll->sync_src >= 0) { - ret = arizona_calc_fll(fll, &sync, fll->sync_freq, - fll->fout); - if (ret != 0) - return ret; - } + if (fll->fout && Fref > 0) { + ret = arizona_validate_fll(fll, Fref, fll->fout); + if (ret != 0) + return ret; } fll->ref_src = source; fll->ref_freq = Fref; if (fll->fout && Fref > 0) { - arizona_enable_fll(fll, &ref, &sync); + arizona_enable_fll(fll); } return 0; @@ -1655,7 +1666,6 @@ EXPORT_SYMBOL_GPL(arizona_set_fll_refclk); int arizona_set_fll(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - struct arizona_fll_cfg ref, sync; int ret; if (fll->sync_src == source && @@ -1664,13 +1674,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source, if (Fout) { if (fll->ref_src >= 0) { - ret = arizona_calc_fll(fll, &ref, fll->ref_freq, - Fout); + ret = arizona_validate_fll(fll, fll->ref_freq, Fout); if (ret != 0) return ret; } - ret = arizona_calc_fll(fll, &sync, Fref, Fout); + ret = arizona_validate_fll(fll, Fref, Fout); if (ret != 0) return ret; } @@ -1680,7 +1689,7 @@ int arizona_set_fll(struct arizona_fll *fll, int source, fll->fout = Fout; if (Fout) { - arizona_enable_fll(fll, &ref, &sync); + arizona_enable_fll(fll); } else { arizona_disable_fll(fll); } -- cgit v1.1 From 8ccefcd265b486186c94ea70c77511e7c570347d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:21 +0000 Subject: ASoC: arizona: Don't pass Fout into arizona_calc_fll As we now calculate the FLL configuration at a later stage in the process the fout member of the FLL structure will contain the desired Fout frequency so no need to pass this in seperately. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 7398c69..7b1354a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1408,13 +1408,12 @@ static int arizona_validate_fll(struct arizona_fll *fll, static int arizona_calc_fll(struct arizona_fll *fll, struct arizona_fll_cfg *cfg, - unsigned int Fref, - unsigned int Fout) + unsigned int Fref) { unsigned int target, div, gcd_fll; int i, ratio; - arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); + arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout); /* Fref must be <=13.5MHz */ div = 1; @@ -1432,12 +1431,12 @@ static int arizona_calc_fll(struct arizona_fll *fll, /* Fvco should be over the targt; don't check the upper bound */ div = ARIZONA_FLL_MIN_OUTDIV; - while (Fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { + while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { div++; if (div > ARIZONA_FLL_MAX_OUTDIV) return -EINVAL; } - target = Fout * div / fll->vco_mult; + target = fll->fout * div / fll->vco_mult; cfg->outdiv = div; arizona_fll_dbg(fll, "Fvco=%dHz\n", target); @@ -1565,19 +1564,19 @@ static void arizona_enable_fll(struct arizona_fll *fll) */ if (fll->ref_src >= 0 && fll->ref_freq && fll->ref_src != fll->sync_src) { - arizona_calc_fll(fll, &cfg, fll->ref_freq, fll->fout); + arizona_calc_fll(fll, &cfg, fll->ref_freq); arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, false); if (fll->sync_src >= 0) { - arizona_calc_fll(fll, &cfg, fll->sync_freq, fll->fout); + arizona_calc_fll(fll, &cfg, fll->sync_freq); arizona_apply_fll(arizona, fll->base + 0x10, &cfg, fll->sync_src, true); use_sync = true; } } else if (fll->sync_src >= 0) { - arizona_calc_fll(fll, &cfg, fll->sync_freq, fll->fout); + arizona_calc_fll(fll, &cfg, fll->sync_freq); arizona_apply_fll(arizona, fll->base, &cfg, fll->sync_src, false); -- cgit v1.1 From f641aec62c948c7754429136ad176824fbb97238 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:22 +0000 Subject: ASoC: arizona: Calculate OUTDIV first OUTDIV will remain unchanged whilst the rest of the FLL configuration is calculated so do this first. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 7b1354a..1f106ab 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1415,6 +1415,18 @@ static int arizona_calc_fll(struct arizona_fll *fll, arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout); + /* Fvco should be over the targt; don't check the upper bound */ + div = ARIZONA_FLL_MIN_OUTDIV; + while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { + div++; + if (div > ARIZONA_FLL_MAX_OUTDIV) + return -EINVAL; + } + target = fll->fout * div / fll->vco_mult; + cfg->outdiv = div; + + arizona_fll_dbg(fll, "Fvco=%dHz\n", target); + /* Fref must be <=13.5MHz */ div = 1; cfg->refdiv = 0; @@ -1429,18 +1441,6 @@ static int arizona_calc_fll(struct arizona_fll *fll, /* Apply the division for our remaining calculations */ Fref /= div; - /* Fvco should be over the targt; don't check the upper bound */ - div = ARIZONA_FLL_MIN_OUTDIV; - while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { - div++; - if (div > ARIZONA_FLL_MAX_OUTDIV) - return -EINVAL; - } - target = fll->fout * div / fll->vco_mult; - cfg->outdiv = div; - - arizona_fll_dbg(fll, "Fvco=%dHz\n", target); - /* Find an appropraite FLL_FRATIO and factor it out of the target */ for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { -- cgit v1.1 From 5a3935c7643966e4172e7a704a48a35f9b4dc668 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:23 +0000 Subject: ASoC: arizona: Calculate FLL gain last No part of the FLL calculation depends on the value determined for the gain but the gain does depend on other values. In preparation for future updates this patch moves the gain to be the last thing calculated. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 1f106ab..219d1d5 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1455,18 +1455,6 @@ static int arizona_calc_fll(struct arizona_fll *fll, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { - if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { - cfg->gain = fll_gains[i].gain; - break; - } - } - if (i == ARRAY_SIZE(fll_gains)) { - arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", - Fref); - return -EINVAL; - } - cfg->n = target / (ratio * Fref); if (target % (ratio * Fref)) { @@ -1490,6 +1478,18 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda >>= 1; } + for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { + if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { + cfg->gain = fll_gains[i].gain; + break; + } + } + if (i == ARRAY_SIZE(fll_gains)) { + arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", + Fref); + return -EINVAL; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", -- cgit v1.1 From d0800342bba71e7f11b2a67a29cf00c41ad1a3e4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 7 Mar 2014 16:34:25 +0000 Subject: ASoC: arizona: Support new fratio encoding on the wm5110 rev D The reference clock path on newer IP FLLs requires a different configuration, and should avoid integer mode operation. This patch adds support for both the new encoding and updates the calculation. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 130 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 29 deletions(-) (limited to 'sound/soc/codecs/arizona.c') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 219d1d5..c388486 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -53,8 +53,10 @@ #define ARIZONA_AIF_RX_ENABLES 0x1A #define ARIZONA_AIF_FORCE_WRITE 0x1B +#define ARIZONA_FLL_VCO_CORNER 141900000 #define ARIZONA_FLL_MAX_FREF 13500000 #define ARIZONA_FLL_MIN_FVCO 90000000 +#define ARIZONA_FLL_MAX_FRATIO 16 #define ARIZONA_FLL_MAX_REFDIV 8 #define ARIZONA_FLL_MIN_OUTDIV 2 #define ARIZONA_FLL_MAX_OUTDIV 7 @@ -1406,9 +1408,99 @@ static int arizona_validate_fll(struct arizona_fll *fll, return 0; } +static int arizona_find_fratio(unsigned int Fref, int *fratio) +{ + int i; + + /* Find an appropriate FLL_FRATIO */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + if (fratio) + *fratio = fll_fratios[i].fratio; + return fll_fratios[i].ratio; + } + } + + return -EINVAL; +} + +static int arizona_calc_fratio(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int target, + unsigned int Fref, bool sync) +{ + int init_ratio, ratio; + int refdiv, div; + + /* Fref must be <=13.5MHz, find initial refdiv */ + div = 1; + cfg->refdiv = 0; + while (Fref > ARIZONA_FLL_MAX_FREF) { + div *= 2; + Fref /= 2; + cfg->refdiv++; + + if (div > ARIZONA_FLL_MAX_REFDIV) + return -EINVAL; + } + + /* Find an appropriate FLL_FRATIO */ + init_ratio = arizona_find_fratio(Fref, &cfg->fratio); + if (init_ratio < 0) { + arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", + Fref); + return init_ratio; + } + + switch (fll->arizona->type) { + case WM5110: + if (fll->arizona->rev < 3 || sync) + return init_ratio; + break; + default: + return init_ratio; + } + + cfg->fratio = init_ratio - 1; + + /* Adjust FRATIO/refdiv to avoid integer mode if possible */ + refdiv = cfg->refdiv; + + while (div <= ARIZONA_FLL_MAX_REFDIV) { + for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; + ratio++) { + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } + } + + for (ratio = init_ratio - 1; ratio >= 0; ratio--) { + if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < + Fref) + break; + + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } + } + + div *= 2; + Fref /= 2; + refdiv++; + init_ratio = arizona_find_fratio(Fref, NULL); + } + + arizona_fll_warn(fll, "Falling back to integer mode operation\n"); + return cfg->fratio + 1; +} + static int arizona_calc_fll(struct arizona_fll *fll, struct arizona_fll_cfg *cfg, - unsigned int Fref) + unsigned int Fref, bool sync) { unsigned int target, div, gcd_fll; int i, ratio; @@ -1427,33 +1519,13 @@ static int arizona_calc_fll(struct arizona_fll *fll, arizona_fll_dbg(fll, "Fvco=%dHz\n", target); - /* Fref must be <=13.5MHz */ - div = 1; - cfg->refdiv = 0; - while ((Fref / div) > ARIZONA_FLL_MAX_FREF) { - div *= 2; - cfg->refdiv++; - - if (div > ARIZONA_FLL_MAX_REFDIV) - return -EINVAL; - } + /* Find an appropriate FLL_FRATIO and refdiv */ + ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); + if (ratio < 0) + return ratio; /* Apply the division for our remaining calculations */ - Fref /= div; - - /* Find an appropraite FLL_FRATIO and factor it out of the target */ - for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { - if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { - cfg->fratio = fll_fratios[i].fratio; - ratio = fll_fratios[i].ratio; - break; - } - } - if (i == ARRAY_SIZE(fll_fratios)) { - arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", - Fref); - return -EINVAL; - } + Fref = Fref / (1 << cfg->refdiv); cfg->n = target / (ratio * Fref); @@ -1564,19 +1636,19 @@ static void arizona_enable_fll(struct arizona_fll *fll) */ if (fll->ref_src >= 0 && fll->ref_freq && fll->ref_src != fll->sync_src) { - arizona_calc_fll(fll, &cfg, fll->ref_freq); + arizona_calc_fll(fll, &cfg, fll->ref_freq, false); arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, false); if (fll->sync_src >= 0) { - arizona_calc_fll(fll, &cfg, fll->sync_freq); + arizona_calc_fll(fll, &cfg, fll->sync_freq, true); arizona_apply_fll(arizona, fll->base + 0x10, &cfg, fll->sync_src, true); use_sync = true; } } else if (fll->sync_src >= 0) { - arizona_calc_fll(fll, &cfg, fll->sync_freq); + arizona_calc_fll(fll, &cfg, fll->sync_freq, false); arizona_apply_fll(arizona, fll->base, &cfg, fll->sync_src, false); -- cgit v1.1