aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/arizona.c130
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
1411static 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
1427static 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
1409static int arizona_calc_fll(struct arizona_fll *fll, 1501static 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);