|
@@ -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);
|