diff options
| author | Charles Keepax <ckeepax@opensource.wolfsonmicro.com> | 2014-03-07 11:34:25 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@linaro.org> | 2014-03-09 04:28:07 -0400 |
| commit | d0800342bba71e7f11b2a67a29cf00c41ad1a3e4 (patch) | |
| tree | bef893cc8b8688bad78b09b6ae551376848500f5 | |
| parent | 9dd02e89cb920a727349f8852430fdc92cc729f4 (diff) | |
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 <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
| -rw-r--r-- | sound/soc/codecs/arizona.c | 130 |
1 files changed, 101 insertions, 29 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 219d1d54f536..c3884861e8cb 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c | |||
| @@ -53,8 +53,10 @@ | |||
| 53 | #define ARIZONA_AIF_RX_ENABLES 0x1A | 53 | #define ARIZONA_AIF_RX_ENABLES 0x1A |
| 54 | #define ARIZONA_AIF_FORCE_WRITE 0x1B | 54 | #define ARIZONA_AIF_FORCE_WRITE 0x1B |
| 55 | 55 | ||
| 56 | #define ARIZONA_FLL_VCO_CORNER 141900000 | ||
| 56 | #define ARIZONA_FLL_MAX_FREF 13500000 | 57 | #define ARIZONA_FLL_MAX_FREF 13500000 |
| 57 | #define ARIZONA_FLL_MIN_FVCO 90000000 | 58 | #define ARIZONA_FLL_MIN_FVCO 90000000 |
| 59 | #define ARIZONA_FLL_MAX_FRATIO 16 | ||
| 58 | #define ARIZONA_FLL_MAX_REFDIV 8 | 60 | #define ARIZONA_FLL_MAX_REFDIV 8 |
| 59 | #define ARIZONA_FLL_MIN_OUTDIV 2 | 61 | #define ARIZONA_FLL_MIN_OUTDIV 2 |
| 60 | #define ARIZONA_FLL_MAX_OUTDIV 7 | 62 | #define ARIZONA_FLL_MAX_OUTDIV 7 |
| @@ -1406,9 +1408,99 @@ static int arizona_validate_fll(struct arizona_fll *fll, | |||
| 1406 | return 0; | 1408 | return 0; |
| 1407 | } | 1409 | } |
| 1408 | 1410 | ||
| 1411 | static int arizona_find_fratio(unsigned int Fref, int *fratio) | ||
| 1412 | { | ||
| 1413 | int i; | ||
| 1414 | |||
| 1415 | /* Find an appropriate FLL_FRATIO */ | ||
| 1416 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | ||
| 1417 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | ||
| 1418 | if (fratio) | ||
| 1419 | *fratio = fll_fratios[i].fratio; | ||
| 1420 | return fll_fratios[i].ratio; | ||
| 1421 | } | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | return -EINVAL; | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | static int arizona_calc_fratio(struct arizona_fll *fll, | ||
| 1428 | struct arizona_fll_cfg *cfg, | ||
| 1429 | unsigned int target, | ||
| 1430 | unsigned int Fref, bool sync) | ||
| 1431 | { | ||
| 1432 | int init_ratio, ratio; | ||
| 1433 | int refdiv, div; | ||
| 1434 | |||
| 1435 | /* Fref must be <=13.5MHz, find initial refdiv */ | ||
| 1436 | div = 1; | ||
| 1437 | cfg->refdiv = 0; | ||
| 1438 | while (Fref > ARIZONA_FLL_MAX_FREF) { | ||
| 1439 | div *= 2; | ||
| 1440 | Fref /= 2; | ||
| 1441 | cfg->refdiv++; | ||
| 1442 | |||
| 1443 | if (div > ARIZONA_FLL_MAX_REFDIV) | ||
| 1444 | return -EINVAL; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | /* Find an appropriate FLL_FRATIO */ | ||
| 1448 | init_ratio = arizona_find_fratio(Fref, &cfg->fratio); | ||
| 1449 | if (init_ratio < 0) { | ||
| 1450 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
| 1451 | Fref); | ||
| 1452 | return init_ratio; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | switch (fll->arizona->type) { | ||
| 1456 | case WM5110: | ||
| 1457 | if (fll->arizona->rev < 3 || sync) | ||
| 1458 | return init_ratio; | ||
| 1459 | break; | ||
| 1460 | default: | ||
| 1461 | return init_ratio; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | cfg->fratio = init_ratio - 1; | ||
| 1465 | |||
| 1466 | /* Adjust FRATIO/refdiv to avoid integer mode if possible */ | ||
| 1467 | refdiv = cfg->refdiv; | ||
| 1468 | |||
| 1469 | while (div <= ARIZONA_FLL_MAX_REFDIV) { | ||
| 1470 | for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; | ||
| 1471 | ratio++) { | ||
| 1472 | if (target % (ratio * Fref)) { | ||
| 1473 | cfg->refdiv = refdiv; | ||
| 1474 | cfg->fratio = ratio - 1; | ||
| 1475 | return ratio; | ||
| 1476 | } | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | for (ratio = init_ratio - 1; ratio >= 0; ratio--) { | ||
| 1480 | if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < | ||
| 1481 | Fref) | ||
| 1482 | break; | ||
| 1483 | |||
| 1484 | if (target % (ratio * Fref)) { | ||
| 1485 | cfg->refdiv = refdiv; | ||
| 1486 | cfg->fratio = ratio - 1; | ||
| 1487 | return ratio; | ||
| 1488 | } | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | div *= 2; | ||
| 1492 | Fref /= 2; | ||
| 1493 | refdiv++; | ||
| 1494 | init_ratio = arizona_find_fratio(Fref, NULL); | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | arizona_fll_warn(fll, "Falling back to integer mode operation\n"); | ||
| 1498 | return cfg->fratio + 1; | ||
| 1499 | } | ||
| 1500 | |||
| 1409 | static int arizona_calc_fll(struct arizona_fll *fll, | 1501 | static int arizona_calc_fll(struct arizona_fll *fll, |
| 1410 | struct arizona_fll_cfg *cfg, | 1502 | struct arizona_fll_cfg *cfg, |
| 1411 | unsigned int Fref) | 1503 | unsigned int Fref, bool sync) |
| 1412 | { | 1504 | { |
| 1413 | unsigned int target, div, gcd_fll; | 1505 | unsigned int target, div, gcd_fll; |
| 1414 | int i, ratio; | 1506 | int i, ratio; |
| @@ -1427,33 +1519,13 @@ static int arizona_calc_fll(struct arizona_fll *fll, | |||
| 1427 | 1519 | ||
| 1428 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); | 1520 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); |
| 1429 | 1521 | ||
| 1430 | /* Fref must be <=13.5MHz */ | 1522 | /* Find an appropriate FLL_FRATIO and refdiv */ |
| 1431 | div = 1; | 1523 | ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); |
| 1432 | cfg->refdiv = 0; | 1524 | if (ratio < 0) |
| 1433 | while ((Fref / div) > ARIZONA_FLL_MAX_FREF) { | 1525 | return ratio; |
| 1434 | div *= 2; | ||
| 1435 | cfg->refdiv++; | ||
| 1436 | |||
| 1437 | if (div > ARIZONA_FLL_MAX_REFDIV) | ||
| 1438 | return -EINVAL; | ||
| 1439 | } | ||
| 1440 | 1526 | ||
| 1441 | /* Apply the division for our remaining calculations */ | 1527 | /* Apply the division for our remaining calculations */ |
| 1442 | Fref /= div; | 1528 | Fref = Fref / (1 << cfg->refdiv); |
| 1443 | |||
| 1444 | /* Find an appropraite FLL_FRATIO and factor it out of the target */ | ||
| 1445 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | ||
| 1446 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | ||
| 1447 | cfg->fratio = fll_fratios[i].fratio; | ||
| 1448 | ratio = fll_fratios[i].ratio; | ||
| 1449 | break; | ||
| 1450 | } | ||
| 1451 | } | ||
| 1452 | if (i == ARRAY_SIZE(fll_fratios)) { | ||
| 1453 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
| 1454 | Fref); | ||
| 1455 | return -EINVAL; | ||
| 1456 | } | ||
| 1457 | 1529 | ||
| 1458 | cfg->n = target / (ratio * Fref); | 1530 | cfg->n = target / (ratio * Fref); |
| 1459 | 1531 | ||
| @@ -1564,19 +1636,19 @@ static void arizona_enable_fll(struct arizona_fll *fll) | |||
| 1564 | */ | 1636 | */ |
| 1565 | if (fll->ref_src >= 0 && fll->ref_freq && | 1637 | if (fll->ref_src >= 0 && fll->ref_freq && |
| 1566 | fll->ref_src != fll->sync_src) { | 1638 | fll->ref_src != fll->sync_src) { |
| 1567 | arizona_calc_fll(fll, &cfg, fll->ref_freq); | 1639 | arizona_calc_fll(fll, &cfg, fll->ref_freq, false); |
| 1568 | 1640 | ||
| 1569 | arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, | 1641 | arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, |
| 1570 | false); | 1642 | false); |
| 1571 | if (fll->sync_src >= 0) { | 1643 | if (fll->sync_src >= 0) { |
| 1572 | arizona_calc_fll(fll, &cfg, fll->sync_freq); | 1644 | arizona_calc_fll(fll, &cfg, fll->sync_freq, true); |
| 1573 | 1645 | ||
| 1574 | arizona_apply_fll(arizona, fll->base + 0x10, &cfg, | 1646 | arizona_apply_fll(arizona, fll->base + 0x10, &cfg, |
| 1575 | fll->sync_src, true); | 1647 | fll->sync_src, true); |
| 1576 | use_sync = true; | 1648 | use_sync = true; |
| 1577 | } | 1649 | } |
| 1578 | } else if (fll->sync_src >= 0) { | 1650 | } else if (fll->sync_src >= 0) { |
| 1579 | arizona_calc_fll(fll, &cfg, fll->sync_freq); | 1651 | arizona_calc_fll(fll, &cfg, fll->sync_freq, false); |
| 1580 | 1652 | ||
| 1581 | arizona_apply_fll(arizona, fll->base, &cfg, | 1653 | arizona_apply_fll(arizona, fll->base, &cfg, |
| 1582 | fll->sync_src, false); | 1654 | fll->sync_src, false); |
