aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r--sound/soc/codecs/twl4030.c173
1 files changed, 168 insertions, 5 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index a1b76d7fd13..cc2968cf640 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
1487static 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
1523static 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
1559static 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
1581static 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
1498struct snd_soc_dai twl4030_dai = { 1636static 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
1643struct 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};
1514EXPORT_SYMBOL_GPL(twl4030_dai); 1677EXPORT_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
1646static int __init twl4030_modinit(void) 1809static 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}
1650module_init(twl4030_modinit); 1813module_init(twl4030_modinit);
1651 1814
1652static void __exit twl4030_exit(void) 1815static 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}
1656module_exit(twl4030_exit); 1819module_exit(twl4030_exit);
1657 1820