Commit d0800342 authored by Charles Keepax's avatar Charles Keepax Committed by Mark Brown

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: default avatarCharles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 9dd02e89
...@@ -53,8 +53,10 @@ ...@@ -53,8 +53,10 @@
#define ARIZONA_AIF_RX_ENABLES 0x1A #define ARIZONA_AIF_RX_ENABLES 0x1A
#define ARIZONA_AIF_FORCE_WRITE 0x1B #define ARIZONA_AIF_FORCE_WRITE 0x1B
#define ARIZONA_FLL_VCO_CORNER 141900000
#define ARIZONA_FLL_MAX_FREF 13500000 #define ARIZONA_FLL_MAX_FREF 13500000
#define ARIZONA_FLL_MIN_FVCO 90000000 #define ARIZONA_FLL_MIN_FVCO 90000000
#define ARIZONA_FLL_MAX_FRATIO 16
#define ARIZONA_FLL_MAX_REFDIV 8 #define ARIZONA_FLL_MAX_REFDIV 8
#define ARIZONA_FLL_MIN_OUTDIV 2 #define ARIZONA_FLL_MIN_OUTDIV 2
#define ARIZONA_FLL_MAX_OUTDIV 7 #define ARIZONA_FLL_MAX_OUTDIV 7
...@@ -1406,9 +1408,99 @@ static int arizona_validate_fll(struct arizona_fll *fll, ...@@ -1406,9 +1408,99 @@ static int arizona_validate_fll(struct arizona_fll *fll,
return 0; 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, static int arizona_calc_fll(struct arizona_fll *fll,
struct arizona_fll_cfg *cfg, struct arizona_fll_cfg *cfg,
unsigned int Fref) unsigned int Fref, bool sync)
{ {
unsigned int target, div, gcd_fll; unsigned int target, div, gcd_fll;
int i, ratio; int i, ratio;
...@@ -1427,33 +1519,13 @@ static int arizona_calc_fll(struct arizona_fll *fll, ...@@ -1427,33 +1519,13 @@ static int arizona_calc_fll(struct arizona_fll *fll,
arizona_fll_dbg(fll, "Fvco=%dHz\n", target); arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
/* Fref must be <=13.5MHz */ /* Find an appropriate FLL_FRATIO and refdiv */
div = 1; ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync);
cfg->refdiv = 0; if (ratio < 0)
while ((Fref / div) > ARIZONA_FLL_MAX_FREF) { return ratio;
div *= 2;
cfg->refdiv++;
if (div > ARIZONA_FLL_MAX_REFDIV)
return -EINVAL;
}
/* Apply the division for our remaining calculations */ /* Apply the division for our remaining calculations */
Fref /= div; Fref = Fref / (1 << cfg->refdiv);
/* 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;
}
cfg->n = target / (ratio * Fref); cfg->n = target / (ratio * Fref);
...@@ -1564,19 +1636,19 @@ static void arizona_enable_fll(struct arizona_fll *fll) ...@@ -1564,19 +1636,19 @@ static void arizona_enable_fll(struct arizona_fll *fll)
*/ */
if (fll->ref_src >= 0 && fll->ref_freq && if (fll->ref_src >= 0 && fll->ref_freq &&
fll->ref_src != fll->sync_src) { 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, arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src,
false); false);
if (fll->sync_src >= 0) { 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, arizona_apply_fll(arizona, fll->base + 0x10, &cfg,
fll->sync_src, true); fll->sync_src, true);
use_sync = true; use_sync = true;
} }
} else if (fll->sync_src >= 0) { } 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, arizona_apply_fll(arizona, fll->base, &cfg,
fll->sync_src, false); fll->sync_src, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment