diff options
Diffstat (limited to 'sound/soc/codecs/arizona.c')
-rw-r--r-- | sound/soc/codecs/arizona.c | 254 |
1 files changed, 168 insertions, 86 deletions
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index a32b84ac03f6..29e198f57d4c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c | |||
@@ -53,6 +53,14 @@ | |||
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 | ||
57 | #define ARIZONA_FLL_MAX_FREF 13500000 | ||
58 | #define ARIZONA_FLL_MIN_FVCO 90000000 | ||
59 | #define ARIZONA_FLL_MAX_FRATIO 16 | ||
60 | #define ARIZONA_FLL_MAX_REFDIV 8 | ||
61 | #define ARIZONA_FLL_MIN_OUTDIV 2 | ||
62 | #define ARIZONA_FLL_MAX_OUTDIV 7 | ||
63 | |||
56 | #define arizona_fll_err(_fll, fmt, ...) \ | 64 | #define arizona_fll_err(_fll, fmt, ...) \ |
57 | dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) | 65 | dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) |
58 | #define arizona_fll_warn(_fll, fmt, ...) \ | 66 | #define arizona_fll_warn(_fll, fmt, ...) \ |
@@ -1386,74 +1394,147 @@ struct arizona_fll_cfg { | |||
1386 | int gain; | 1394 | int gain; |
1387 | }; | 1395 | }; |
1388 | 1396 | ||
1389 | static int arizona_calc_fll(struct arizona_fll *fll, | 1397 | static int arizona_validate_fll(struct arizona_fll *fll, |
1390 | struct arizona_fll_cfg *cfg, | 1398 | unsigned int Fref, |
1391 | unsigned int Fref, | 1399 | unsigned int Fout) |
1392 | unsigned int Fout) | ||
1393 | { | 1400 | { |
1394 | unsigned int target, div, gcd_fll; | 1401 | unsigned int Fvco_min; |
1395 | int i, ratio; | 1402 | |
1403 | if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) { | ||
1404 | arizona_fll_err(fll, | ||
1405 | "Can't scale %dMHz in to <=13.5MHz\n", | ||
1406 | Fref); | ||
1407 | return -EINVAL; | ||
1408 | } | ||
1396 | 1409 | ||
1397 | arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout); | 1410 | Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult; |
1411 | if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) { | ||
1412 | arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", | ||
1413 | Fout); | ||
1414 | return -EINVAL; | ||
1415 | } | ||
1416 | |||
1417 | return 0; | ||
1418 | } | ||
1419 | |||
1420 | static int arizona_find_fratio(unsigned int Fref, int *fratio) | ||
1421 | { | ||
1422 | int i; | ||
1423 | |||
1424 | /* Find an appropriate FLL_FRATIO */ | ||
1425 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | ||
1426 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | ||
1427 | if (fratio) | ||
1428 | *fratio = fll_fratios[i].fratio; | ||
1429 | return fll_fratios[i].ratio; | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | return -EINVAL; | ||
1434 | } | ||
1435 | |||
1436 | static int arizona_calc_fratio(struct arizona_fll *fll, | ||
1437 | struct arizona_fll_cfg *cfg, | ||
1438 | unsigned int target, | ||
1439 | unsigned int Fref, bool sync) | ||
1440 | { | ||
1441 | int init_ratio, ratio; | ||
1442 | int refdiv, div; | ||
1398 | 1443 | ||
1399 | /* Fref must be <=13.5MHz */ | 1444 | /* Fref must be <=13.5MHz, find initial refdiv */ |
1400 | div = 1; | 1445 | div = 1; |
1401 | cfg->refdiv = 0; | 1446 | cfg->refdiv = 0; |
1402 | while ((Fref / div) > 13500000) { | 1447 | while (Fref > ARIZONA_FLL_MAX_FREF) { |
1403 | div *= 2; | 1448 | div *= 2; |
1449 | Fref /= 2; | ||
1404 | cfg->refdiv++; | 1450 | cfg->refdiv++; |
1405 | 1451 | ||
1406 | if (div > 8) { | 1452 | if (div > ARIZONA_FLL_MAX_REFDIV) |
1407 | arizona_fll_err(fll, | ||
1408 | "Can't scale %dMHz in to <=13.5MHz\n", | ||
1409 | Fref); | ||
1410 | return -EINVAL; | 1453 | return -EINVAL; |
1454 | } | ||
1455 | |||
1456 | /* Find an appropriate FLL_FRATIO */ | ||
1457 | init_ratio = arizona_find_fratio(Fref, &cfg->fratio); | ||
1458 | if (init_ratio < 0) { | ||
1459 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
1460 | Fref); | ||
1461 | return init_ratio; | ||
1462 | } | ||
1463 | |||
1464 | switch (fll->arizona->type) { | ||
1465 | case WM5110: | ||
1466 | if (fll->arizona->rev < 3 || sync) | ||
1467 | return init_ratio; | ||
1468 | break; | ||
1469 | default: | ||
1470 | return init_ratio; | ||
1471 | } | ||
1472 | |||
1473 | cfg->fratio = init_ratio - 1; | ||
1474 | |||
1475 | /* Adjust FRATIO/refdiv to avoid integer mode if possible */ | ||
1476 | refdiv = cfg->refdiv; | ||
1477 | |||
1478 | while (div <= ARIZONA_FLL_MAX_REFDIV) { | ||
1479 | for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; | ||
1480 | ratio++) { | ||
1481 | if (target % (ratio * Fref)) { | ||
1482 | cfg->refdiv = refdiv; | ||
1483 | cfg->fratio = ratio - 1; | ||
1484 | return ratio; | ||
1485 | } | ||
1411 | } | 1486 | } |
1487 | |||
1488 | for (ratio = init_ratio - 1; ratio >= 0; ratio--) { | ||
1489 | if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < | ||
1490 | Fref) | ||
1491 | break; | ||
1492 | |||
1493 | if (target % (ratio * Fref)) { | ||
1494 | cfg->refdiv = refdiv; | ||
1495 | cfg->fratio = ratio - 1; | ||
1496 | return ratio; | ||
1497 | } | ||
1498 | } | ||
1499 | |||
1500 | div *= 2; | ||
1501 | Fref /= 2; | ||
1502 | refdiv++; | ||
1503 | init_ratio = arizona_find_fratio(Fref, NULL); | ||
1412 | } | 1504 | } |
1413 | 1505 | ||
1414 | /* Apply the division for our remaining calculations */ | 1506 | arizona_fll_warn(fll, "Falling back to integer mode operation\n"); |
1415 | Fref /= div; | 1507 | return cfg->fratio + 1; |
1508 | } | ||
1509 | |||
1510 | static int arizona_calc_fll(struct arizona_fll *fll, | ||
1511 | struct arizona_fll_cfg *cfg, | ||
1512 | unsigned int Fref, bool sync) | ||
1513 | { | ||
1514 | unsigned int target, div, gcd_fll; | ||
1515 | int i, ratio; | ||
1516 | |||
1517 | arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout); | ||
1416 | 1518 | ||
1417 | /* Fvco should be over the targt; don't check the upper bound */ | 1519 | /* Fvco should be over the targt; don't check the upper bound */ |
1418 | div = 1; | 1520 | div = ARIZONA_FLL_MIN_OUTDIV; |
1419 | while (Fout * div < 90000000 * fll->vco_mult) { | 1521 | while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { |
1420 | div++; | 1522 | div++; |
1421 | if (div > 7) { | 1523 | if (div > ARIZONA_FLL_MAX_OUTDIV) |
1422 | arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", | ||
1423 | Fout); | ||
1424 | return -EINVAL; | 1524 | return -EINVAL; |
1425 | } | ||
1426 | } | 1525 | } |
1427 | target = Fout * div / fll->vco_mult; | 1526 | target = fll->fout * div / fll->vco_mult; |
1428 | cfg->outdiv = div; | 1527 | cfg->outdiv = div; |
1429 | 1528 | ||
1430 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); | 1529 | arizona_fll_dbg(fll, "Fvco=%dHz\n", target); |
1431 | 1530 | ||
1432 | /* Find an appropraite FLL_FRATIO and factor it out of the target */ | 1531 | /* Find an appropriate FLL_FRATIO and refdiv */ |
1433 | for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { | 1532 | ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); |
1434 | if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { | 1533 | if (ratio < 0) |
1435 | cfg->fratio = fll_fratios[i].fratio; | 1534 | return ratio; |
1436 | ratio = fll_fratios[i].ratio; | ||
1437 | break; | ||
1438 | } | ||
1439 | } | ||
1440 | if (i == ARRAY_SIZE(fll_fratios)) { | ||
1441 | arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", | ||
1442 | Fref); | ||
1443 | return -EINVAL; | ||
1444 | } | ||
1445 | 1535 | ||
1446 | for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { | 1536 | /* Apply the division for our remaining calculations */ |
1447 | if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { | 1537 | Fref = Fref / (1 << cfg->refdiv); |
1448 | cfg->gain = fll_gains[i].gain; | ||
1449 | break; | ||
1450 | } | ||
1451 | } | ||
1452 | if (i == ARRAY_SIZE(fll_gains)) { | ||
1453 | arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", | ||
1454 | Fref); | ||
1455 | return -EINVAL; | ||
1456 | } | ||
1457 | 1538 | ||
1458 | cfg->n = target / (ratio * Fref); | 1539 | cfg->n = target / (ratio * Fref); |
1459 | 1540 | ||
@@ -1478,6 +1559,18 @@ static int arizona_calc_fll(struct arizona_fll *fll, | |||
1478 | cfg->lambda >>= 1; | 1559 | cfg->lambda >>= 1; |
1479 | } | 1560 | } |
1480 | 1561 | ||
1562 | for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { | ||
1563 | if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { | ||
1564 | cfg->gain = fll_gains[i].gain; | ||
1565 | break; | ||
1566 | } | ||
1567 | } | ||
1568 | if (i == ARRAY_SIZE(fll_gains)) { | ||
1569 | arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", | ||
1570 | Fref); | ||
1571 | return -EINVAL; | ||
1572 | } | ||
1573 | |||
1481 | arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", | 1574 | arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", |
1482 | cfg->n, cfg->theta, cfg->lambda); | 1575 | cfg->n, cfg->theta, cfg->lambda); |
1483 | arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", | 1576 | arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", |
@@ -1505,14 +1598,18 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base, | |||
1505 | cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | | 1598 | cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | |
1506 | source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); | 1599 | source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); |
1507 | 1600 | ||
1508 | if (sync) | 1601 | if (sync) { |
1509 | regmap_update_bits_async(arizona->regmap, base + 0x7, | 1602 | regmap_update_bits(arizona->regmap, base + 0x7, |
1510 | ARIZONA_FLL1_GAIN_MASK, | 1603 | ARIZONA_FLL1_GAIN_MASK, |
1511 | cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); | 1604 | cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); |
1512 | else | 1605 | } else { |
1513 | regmap_update_bits_async(arizona->regmap, base + 0x9, | 1606 | regmap_update_bits(arizona->regmap, base + 0x5, |
1514 | ARIZONA_FLL1_GAIN_MASK, | 1607 | ARIZONA_FLL1_OUTDIV_MASK, |
1515 | cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); | 1608 | cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); |
1609 | regmap_update_bits(arizona->regmap, base + 0x9, | ||
1610 | ARIZONA_FLL1_GAIN_MASK, | ||
1611 | cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); | ||
1612 | } | ||
1516 | 1613 | ||
1517 | regmap_update_bits_async(arizona->regmap, base + 2, | 1614 | regmap_update_bits_async(arizona->regmap, base + 2, |
1518 | ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, | 1615 | ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, |
@@ -1535,13 +1632,12 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll) | |||
1535 | return reg & ARIZONA_FLL1_ENA; | 1632 | return reg & ARIZONA_FLL1_ENA; |
1536 | } | 1633 | } |
1537 | 1634 | ||
1538 | static void arizona_enable_fll(struct arizona_fll *fll, | 1635 | static void arizona_enable_fll(struct arizona_fll *fll) |
1539 | struct arizona_fll_cfg *ref, | ||
1540 | struct arizona_fll_cfg *sync) | ||
1541 | { | 1636 | { |
1542 | struct arizona *arizona = fll->arizona; | 1637 | struct arizona *arizona = fll->arizona; |
1543 | int ret; | 1638 | int ret; |
1544 | bool use_sync = false; | 1639 | bool use_sync = false; |
1640 | struct arizona_fll_cfg cfg; | ||
1545 | 1641 | ||
1546 | /* | 1642 | /* |
1547 | * If we have both REFCLK and SYNCCLK then enable both, | 1643 | * If we have both REFCLK and SYNCCLK then enable both, |
@@ -1549,23 +1645,21 @@ static void arizona_enable_fll(struct arizona_fll *fll, | |||
1549 | */ | 1645 | */ |
1550 | if (fll->ref_src >= 0 && fll->ref_freq && | 1646 | if (fll->ref_src >= 0 && fll->ref_freq && |
1551 | fll->ref_src != fll->sync_src) { | 1647 | fll->ref_src != fll->sync_src) { |
1552 | regmap_update_bits_async(arizona->regmap, fll->base + 5, | 1648 | arizona_calc_fll(fll, &cfg, fll->ref_freq, false); |
1553 | ARIZONA_FLL1_OUTDIV_MASK, | ||
1554 | ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); | ||
1555 | 1649 | ||
1556 | arizona_apply_fll(arizona, fll->base, ref, fll->ref_src, | 1650 | arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, |
1557 | false); | 1651 | false); |
1558 | if (fll->sync_src >= 0) { | 1652 | if (fll->sync_src >= 0) { |
1559 | arizona_apply_fll(arizona, fll->base + 0x10, sync, | 1653 | arizona_calc_fll(fll, &cfg, fll->sync_freq, true); |
1654 | |||
1655 | arizona_apply_fll(arizona, fll->base + 0x10, &cfg, | ||
1560 | fll->sync_src, true); | 1656 | fll->sync_src, true); |
1561 | use_sync = true; | 1657 | use_sync = true; |
1562 | } | 1658 | } |
1563 | } else if (fll->sync_src >= 0) { | 1659 | } else if (fll->sync_src >= 0) { |
1564 | regmap_update_bits_async(arizona->regmap, fll->base + 5, | 1660 | arizona_calc_fll(fll, &cfg, fll->sync_freq, false); |
1565 | ARIZONA_FLL1_OUTDIV_MASK, | ||
1566 | sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); | ||
1567 | 1661 | ||
1568 | arizona_apply_fll(arizona, fll->base, sync, | 1662 | arizona_apply_fll(arizona, fll->base, &cfg, |
1569 | fll->sync_src, false); | 1663 | fll->sync_src, false); |
1570 | 1664 | ||
1571 | regmap_update_bits_async(arizona->regmap, fll->base + 0x11, | 1665 | regmap_update_bits_async(arizona->regmap, fll->base + 0x11, |
@@ -1627,32 +1721,22 @@ static void arizona_disable_fll(struct arizona_fll *fll) | |||
1627 | int arizona_set_fll_refclk(struct arizona_fll *fll, int source, | 1721 | int arizona_set_fll_refclk(struct arizona_fll *fll, int source, |
1628 | unsigned int Fref, unsigned int Fout) | 1722 | unsigned int Fref, unsigned int Fout) |
1629 | { | 1723 | { |
1630 | struct arizona_fll_cfg ref, sync; | ||
1631 | int ret; | 1724 | int ret; |
1632 | 1725 | ||
1633 | if (fll->ref_src == source && fll->ref_freq == Fref) | 1726 | if (fll->ref_src == source && fll->ref_freq == Fref) |
1634 | return 0; | 1727 | return 0; |
1635 | 1728 | ||
1636 | if (fll->fout) { | 1729 | if (fll->fout && Fref > 0) { |
1637 | if (Fref > 0) { | 1730 | ret = arizona_validate_fll(fll, Fref, fll->fout); |
1638 | ret = arizona_calc_fll(fll, &ref, Fref, fll->fout); | 1731 | if (ret != 0) |
1639 | if (ret != 0) | 1732 | return ret; |
1640 | return ret; | ||
1641 | } | ||
1642 | |||
1643 | if (fll->sync_src >= 0) { | ||
1644 | ret = arizona_calc_fll(fll, &sync, fll->sync_freq, | ||
1645 | fll->fout); | ||
1646 | if (ret != 0) | ||
1647 | return ret; | ||
1648 | } | ||
1649 | } | 1733 | } |
1650 | 1734 | ||
1651 | fll->ref_src = source; | 1735 | fll->ref_src = source; |
1652 | fll->ref_freq = Fref; | 1736 | fll->ref_freq = Fref; |
1653 | 1737 | ||
1654 | if (fll->fout && Fref > 0) { | 1738 | if (fll->fout && Fref > 0) { |
1655 | arizona_enable_fll(fll, &ref, &sync); | 1739 | arizona_enable_fll(fll); |
1656 | } | 1740 | } |
1657 | 1741 | ||
1658 | return 0; | 1742 | return 0; |
@@ -1662,7 +1746,6 @@ EXPORT_SYMBOL_GPL(arizona_set_fll_refclk); | |||
1662 | int arizona_set_fll(struct arizona_fll *fll, int source, | 1746 | int arizona_set_fll(struct arizona_fll *fll, int source, |
1663 | unsigned int Fref, unsigned int Fout) | 1747 | unsigned int Fref, unsigned int Fout) |
1664 | { | 1748 | { |
1665 | struct arizona_fll_cfg ref, sync; | ||
1666 | int ret; | 1749 | int ret; |
1667 | 1750 | ||
1668 | if (fll->sync_src == source && | 1751 | if (fll->sync_src == source && |
@@ -1671,13 +1754,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source, | |||
1671 | 1754 | ||
1672 | if (Fout) { | 1755 | if (Fout) { |
1673 | if (fll->ref_src >= 0) { | 1756 | if (fll->ref_src >= 0) { |
1674 | ret = arizona_calc_fll(fll, &ref, fll->ref_freq, | 1757 | ret = arizona_validate_fll(fll, fll->ref_freq, Fout); |
1675 | Fout); | ||
1676 | if (ret != 0) | 1758 | if (ret != 0) |
1677 | return ret; | 1759 | return ret; |
1678 | } | 1760 | } |
1679 | 1761 | ||
1680 | ret = arizona_calc_fll(fll, &sync, Fref, Fout); | 1762 | ret = arizona_validate_fll(fll, Fref, Fout); |
1681 | if (ret != 0) | 1763 | if (ret != 0) |
1682 | return ret; | 1764 | return ret; |
1683 | } | 1765 | } |
@@ -1687,7 +1769,7 @@ int arizona_set_fll(struct arizona_fll *fll, int source, | |||
1687 | fll->fout = Fout; | 1769 | fll->fout = Fout; |
1688 | 1770 | ||
1689 | if (Fout) { | 1771 | if (Fout) { |
1690 | arizona_enable_fll(fll, &ref, &sync); | 1772 | arizona_enable_fll(fll); |
1691 | } else { | 1773 | } else { |
1692 | arizona_disable_fll(fll); | 1774 | arizona_disable_fll(fll); |
1693 | } | 1775 | } |