aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
authorJoonyoung Shim <jy0922.shim@samsung.com>2009-04-20 06:21:35 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-04-20 07:44:24 -0400
commit7154b3e80203ee91f9ba7d0a43d3daa05c49d9e9 (patch)
treed7741c68ea143dc4cfdb2ffbe3ef350e544a12d5 /sound/soc/codecs/twl4030.c
parent6b87a91f5417226c7fe62100b0e7217e7096b789 (diff)
ASoC: TWL4030: Add support Voice DAI
Add Voice DAI to support the PCM voice interface of the twl4030 codec. The PCM voice interface can be used with 8-kHz(voice narrowband) or 16-kHz(voice wideband) sampling rates, and 16bits, and mono RX and mono TX or stereo TX. The PCM voice interface has two modes - PCM mode1 : This uses the normal FS polarity and the rising edge of the clock signal. - PCM mode2 : This uses the FS polarity inverted and the falling edge of the clock signal. If the system master clock is not 26MHz or the twl4030 codec mode is not option2, the voice PCM interface is not available. Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
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 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
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