Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+101 −29
Original line number Diff line number Diff line
@@ -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,54 +1408,124 @@ static int arizona_validate_fll(struct arizona_fll *fll,
	return 0;
}

static int arizona_calc_fll(struct arizona_fll *fll,
			    struct arizona_fll_cfg *cfg,
			    unsigned int Fref)
static int arizona_find_fratio(unsigned int Fref, int *fratio)
{
	unsigned int target, div, gcd_fll;
	int i, ratio;
	int i;

	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout);
	/* 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;
		}
	}

	/* 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);
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 */
	/* Fref must be <=13.5MHz, find initial refdiv */
	div = 1;
	cfg->refdiv = 0;
	while ((Fref / div) > ARIZONA_FLL_MAX_FREF) {
	while (Fref > ARIZONA_FLL_MAX_FREF) {
		div *= 2;
		Fref /= 2;
		cfg->refdiv++;

		if (div > ARIZONA_FLL_MAX_REFDIV)
			return -EINVAL;
	}

	/* Apply the division for our remaining calculations */
	Fref /= div;
	/* 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;
	}

	/* 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;
	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;
			}
	if (i == ARRAY_SIZE(fll_fratios)) {
		arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
				Fref);
		}

		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, bool sync)
{
	unsigned int target, div, gcd_fll;
	int i, ratio;

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

	/* 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 = 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);