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