diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 173 |
1 files changed, 168 insertions, 5 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index a1b76d7fd130..cc2968cf6409 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -1484,6 +1484,144 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
1484 | return 0; | 1484 | return 0; |
1485 | } | 1485 | } |
1486 | 1486 | ||
1487 | static int twl4030_voice_startup(struct snd_pcm_substream *substream, | ||
1488 | struct snd_soc_dai *dai) | ||
1489 | { | ||
1490 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1491 | struct snd_soc_device *socdev = rtd->socdev; | ||
1492 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1493 | u8 infreq; | ||
1494 | u8 mode; | ||
1495 | |||
1496 | /* If the system master clock is not 26MHz, the voice PCM interface is | ||
1497 | * not avilable. | ||
1498 | */ | ||
1499 | infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL) | ||
1500 | & TWL4030_APLL_INFREQ; | ||
1501 | |||
1502 | if (infreq != TWL4030_APLL_INFREQ_26000KHZ) { | ||
1503 | printk(KERN_ERR "TWL4030 voice startup: " | ||
1504 | "MCLK is not 26MHz, call set_sysclk() on init\n"); | ||
1505 | return -EINVAL; | ||
1506 | } | ||
1507 | |||
1508 | /* If the codec mode is not option2, the voice PCM interface is not | ||
1509 | * avilable. | ||
1510 | */ | ||
1511 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | ||
1512 | & TWL4030_OPT_MODE; | ||
1513 | |||
1514 | if (mode != TWL4030_OPTION_2) { | ||
1515 | printk(KERN_ERR "TWL4030 voice startup: " | ||
1516 | "the codec mode is not option2\n"); | ||
1517 | return -EINVAL; | ||
1518 | } | ||
1519 | |||
1520 | return 0; | ||
1521 | } | ||
1522 | |||
1523 | static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | ||
1524 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
1525 | { | ||
1526 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1527 | struct snd_soc_device *socdev = rtd->socdev; | ||
1528 | struct snd_soc_codec *codec = socdev->card->codec; | ||
1529 | u8 old_mode, mode; | ||
1530 | |||
1531 | /* bit rate */ | ||
1532 | old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | ||
1533 | & ~(TWL4030_CODECPDZ); | ||
1534 | mode = old_mode; | ||
1535 | |||
1536 | switch (params_rate(params)) { | ||
1537 | case 8000: | ||
1538 | mode &= ~(TWL4030_SEL_16K); | ||
1539 | break; | ||
1540 | case 16000: | ||
1541 | mode |= TWL4030_SEL_16K; | ||
1542 | break; | ||
1543 | default: | ||
1544 | printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n", | ||
1545 | params_rate(params)); | ||
1546 | return -EINVAL; | ||
1547 | } | ||
1548 | |||
1549 | if (mode != old_mode) { | ||
1550 | /* change rate and set CODECPDZ */ | ||
1551 | twl4030_codec_enable(codec, 0); | ||
1552 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | ||
1553 | twl4030_codec_enable(codec, 1); | ||
1554 | } | ||
1555 | |||
1556 | return 0; | ||
1557 | } | ||
1558 | |||
1559 | static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
1560 | int clk_id, unsigned int freq, int dir) | ||
1561 | { | ||
1562 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1563 | u8 infreq; | ||
1564 | |||
1565 | switch (freq) { | ||
1566 | case 26000000: | ||
1567 | infreq = TWL4030_APLL_INFREQ_26000KHZ; | ||
1568 | break; | ||
1569 | default: | ||
1570 | printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", | ||
1571 | freq); | ||
1572 | return -EINVAL; | ||
1573 | } | ||
1574 | |||
1575 | infreq |= TWL4030_APLL_EN; | ||
1576 | twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); | ||
1577 | |||
1578 | return 0; | ||
1579 | } | ||
1580 | |||
1581 | static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
1582 | unsigned int fmt) | ||
1583 | { | ||
1584 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1585 | u8 old_format, format; | ||
1586 | |||
1587 | /* get format */ | ||
1588 | old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); | ||
1589 | format = old_format; | ||
1590 | |||
1591 | /* set master/slave audio interface */ | ||
1592 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
1593 | case SND_SOC_DAIFMT_CBS_CFM: | ||
1594 | format &= ~(TWL4030_VIF_SLAVE_EN); | ||
1595 | break; | ||
1596 | case SND_SOC_DAIFMT_CBS_CFS: | ||
1597 | format |= TWL4030_VIF_SLAVE_EN; | ||
1598 | break; | ||
1599 | default: | ||
1600 | return -EINVAL; | ||
1601 | } | ||
1602 | |||
1603 | /* clock inversion */ | ||
1604 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
1605 | case SND_SOC_DAIFMT_IB_NF: | ||
1606 | format &= ~(TWL4030_VIF_FORMAT); | ||
1607 | break; | ||
1608 | case SND_SOC_DAIFMT_NB_IF: | ||
1609 | format |= TWL4030_VIF_FORMAT; | ||
1610 | break; | ||
1611 | default: | ||
1612 | return -EINVAL; | ||
1613 | } | ||
1614 | |||
1615 | if (format != old_format) { | ||
1616 | /* change format and set CODECPDZ */ | ||
1617 | twl4030_codec_enable(codec, 0); | ||
1618 | twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | ||
1619 | twl4030_codec_enable(codec, 1); | ||
1620 | } | ||
1621 | |||
1622 | return 0; | ||
1623 | } | ||
1624 | |||
1487 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) | 1625 | #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) |
1488 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) | 1626 | #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) |
1489 | 1627 | ||
@@ -1495,7 +1633,15 @@ static struct snd_soc_dai_ops twl4030_dai_ops = { | |||
1495 | .set_fmt = twl4030_set_dai_fmt, | 1633 | .set_fmt = twl4030_set_dai_fmt, |
1496 | }; | 1634 | }; |
1497 | 1635 | ||
1498 | struct snd_soc_dai twl4030_dai = { | 1636 | static struct snd_soc_dai_ops twl4030_dai_voice_ops = { |
1637 | .startup = twl4030_voice_startup, | ||
1638 | .hw_params = twl4030_voice_hw_params, | ||
1639 | .set_sysclk = twl4030_voice_set_dai_sysclk, | ||
1640 | .set_fmt = twl4030_voice_set_dai_fmt, | ||
1641 | }; | ||
1642 | |||
1643 | struct snd_soc_dai twl4030_dai[] = { | ||
1644 | { | ||
1499 | .name = "twl4030", | 1645 | .name = "twl4030", |
1500 | .playback = { | 1646 | .playback = { |
1501 | .stream_name = "Playback", | 1647 | .stream_name = "Playback", |
@@ -1510,6 +1656,23 @@ struct snd_soc_dai twl4030_dai = { | |||
1510 | .rates = TWL4030_RATES, | 1656 | .rates = TWL4030_RATES, |
1511 | .formats = TWL4030_FORMATS,}, | 1657 | .formats = TWL4030_FORMATS,}, |
1512 | .ops = &twl4030_dai_ops, | 1658 | .ops = &twl4030_dai_ops, |
1659 | }, | ||
1660 | { | ||
1661 | .name = "twl4030 Voice", | ||
1662 | .playback = { | ||
1663 | .stream_name = "Playback", | ||
1664 | .channels_min = 1, | ||
1665 | .channels_max = 1, | ||
1666 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | ||
1667 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
1668 | .capture = { | ||
1669 | .stream_name = "Capture", | ||
1670 | .channels_min = 1, | ||
1671 | .channels_max = 2, | ||
1672 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | ||
1673 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | ||
1674 | .ops = &twl4030_dai_voice_ops, | ||
1675 | }, | ||
1513 | }; | 1676 | }; |
1514 | EXPORT_SYMBOL_GPL(twl4030_dai); | 1677 | EXPORT_SYMBOL_GPL(twl4030_dai); |
1515 | 1678 | ||
@@ -1550,8 +1713,8 @@ static int twl4030_init(struct snd_soc_device *socdev) | |||
1550 | codec->read = twl4030_read_reg_cache; | 1713 | codec->read = twl4030_read_reg_cache; |
1551 | codec->write = twl4030_write; | 1714 | codec->write = twl4030_write; |
1552 | codec->set_bias_level = twl4030_set_bias_level; | 1715 | codec->set_bias_level = twl4030_set_bias_level; |
1553 | codec->dai = &twl4030_dai; | 1716 | codec->dai = twl4030_dai; |
1554 | codec->num_dai = 1; | 1717 | codec->num_dai = ARRAY_SIZE(twl4030_dai), |
1555 | codec->reg_cache_size = sizeof(twl4030_reg); | 1718 | codec->reg_cache_size = sizeof(twl4030_reg); |
1556 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), | 1719 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), |
1557 | GFP_KERNEL); | 1720 | GFP_KERNEL); |
@@ -1645,13 +1808,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | |||
1645 | 1808 | ||
1646 | static int __init twl4030_modinit(void) | 1809 | static int __init twl4030_modinit(void) |
1647 | { | 1810 | { |
1648 | return snd_soc_register_dai(&twl4030_dai); | 1811 | return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); |
1649 | } | 1812 | } |
1650 | module_init(twl4030_modinit); | 1813 | module_init(twl4030_modinit); |
1651 | 1814 | ||
1652 | static void __exit twl4030_exit(void) | 1815 | static void __exit twl4030_exit(void) |
1653 | { | 1816 | { |
1654 | snd_soc_unregister_dai(&twl4030_dai); | 1817 | snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); |
1655 | } | 1818 | } |
1656 | module_exit(twl4030_exit); | 1819 | module_exit(twl4030_exit); |
1657 | 1820 | ||