summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/twl4030.c173
-rw-r--r--sound/soc/codecs/twl4030.h18
-rw-r--r--sound/soc/omap/omap2evm.c2
-rw-r--r--sound/soc/omap/omap3beagle.c2
-rw-r--r--sound/soc/omap/omap3pandora.c4
-rw-r--r--sound/soc/omap/overo.c2
-rw-r--r--sound/soc/omap/sdp3430.c2
7 files changed, 191 insertions, 12 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,
return 0;
}
+static int twl4030_voice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ u8 infreq;
+ u8 mode;
+
+ /* If the system master clock is not 26MHz, the voice PCM interface is
+ * not avilable.
+ */
+ infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
+ & TWL4030_APLL_INFREQ;
+
+ if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
+ printk(KERN_ERR "TWL4030 voice startup: "
+ "MCLK is not 26MHz, call set_sysclk() on init\n");
+ return -EINVAL;
+ }
+
+ /* If the codec mode is not option2, the voice PCM interface is not
+ * avilable.
+ */
+ mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+ & TWL4030_OPT_MODE;
+
+ if (mode != TWL4030_OPTION_2) {
+ printk(KERN_ERR "TWL4030 voice startup: "
+ "the codec mode is not option2\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ u8 old_mode, mode;
+
+ /* bit rate */
+ old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+ & ~(TWL4030_CODECPDZ);
+ mode = old_mode;
+
+ switch (params_rate(params)) {
+ case 8000:
+ mode &= ~(TWL4030_SEL_16K);
+ break;
+ case 16000:
+ mode |= TWL4030_SEL_16K;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (mode != old_mode) {
+ /* change rate and set CODECPDZ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_codec_enable(codec, 1);
+ }
+
+ return 0;
+}
+
+static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 infreq;
+
+ switch (freq) {
+ case 26000000:
+ infreq = TWL4030_APLL_INFREQ_26000KHZ;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ infreq |= TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+ return 0;
+}
+
+static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 old_format, format;
+
+ /* get format */
+ old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+ format = old_format;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFM:
+ format &= ~(TWL4030_VIF_SLAVE_EN);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ format |= TWL4030_VIF_SLAVE_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ format &= ~(TWL4030_VIF_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ format |= TWL4030_VIF_FORMAT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (format != old_format) {
+ /* change format and set CODECPDZ */
+ twl4030_codec_enable(codec, 0);
+ twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+ twl4030_codec_enable(codec, 1);
+ }
+
+ return 0;
+}
+
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
@@ -1495,7 +1633,15 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
.set_fmt = twl4030_set_dai_fmt,
};
-struct snd_soc_dai twl4030_dai = {
+static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
+ .startup = twl4030_voice_startup,
+ .hw_params = twl4030_voice_hw_params,
+ .set_sysclk = twl4030_voice_set_dai_sysclk,
+ .set_fmt = twl4030_voice_set_dai_fmt,
+};
+
+struct snd_soc_dai twl4030_dai[] = {
+{
.name = "twl4030",
.playback = {
.stream_name = "Playback",
@@ -1510,6 +1656,23 @@ struct snd_soc_dai twl4030_dai = {
.rates = TWL4030_RATES,
.formats = TWL4030_FORMATS,},
.ops = &twl4030_dai_ops,
+},
+{
+ .name = "twl4030 Voice",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &twl4030_dai_voice_ops,
+},
};
EXPORT_SYMBOL_GPL(twl4030_dai);
@@ -1550,8 +1713,8 @@ static int twl4030_init(struct snd_soc_device *socdev)
codec->read = twl4030_read_reg_cache;
codec->write = twl4030_write;
codec->set_bias_level = twl4030_set_bias_level;
- codec->dai = &twl4030_dai;
- codec->num_dai = 1;
+ codec->dai = twl4030_dai;
+ codec->num_dai = ARRAY_SIZE(twl4030_dai),
codec->reg_cache_size = sizeof(twl4030_reg);
codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
GFP_KERNEL);
@@ -1645,13 +1808,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void)
{
- return snd_soc_register_dai(&twl4030_dai);
+ return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
}
module_init(twl4030_modinit);
static void __exit twl4030_exit(void)
{
- snd_soc_unregister_dai(&twl4030_dai);
+ snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
}
module_exit(twl4030_exit);
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index cb63765db1df..981ec609495b 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -113,6 +113,8 @@
#define TWL4030_SEL_16K 0x04
#define TWL4030_CODECPDZ 0x02
#define TWL4030_OPT_MODE 0x01
+#define TWL4030_OPTION_1 (1 << 0)
+#define TWL4030_OPTION_2 (0 << 0)
/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
@@ -171,6 +173,17 @@
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01
+/* VOICE_IF (0x0F) Fields */
+
+#define TWL4030_VIF_SLAVE_EN 0x80
+#define TWL4030_VIF_DIN_EN 0x40
+#define TWL4030_VIF_DOUT_EN 0x20
+#define TWL4030_VIF_SWAP 0x10
+#define TWL4030_VIF_FORMAT 0x08
+#define TWL4030_VIF_TRI_EN 0x04
+#define TWL4030_VIF_SUB_EN 0x02
+#define TWL4030_VIF_EN 0x01
+
/* EAR_CTL (0x21) */
#define TWL4030_EAR_GAIN 0x30
@@ -236,7 +249,10 @@
#define TWL4030_SMOOTH_ANAVOL_EN 0x02
#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
-extern struct snd_soc_dai twl4030_dai;
+#define TWL4030_DAI_HIFI 0
+#define TWL4030_DAI_VOICE 1
+
+extern struct snd_soc_dai twl4030_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_twl4030;
#endif /* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
index 0c2322dcf02a..027e1a40f8a1 100644
--- a/sound/soc/omap/omap2evm.c
+++ b/sound/soc/omap/omap2evm.c
@@ -86,7 +86,7 @@ static struct snd_soc_dai_link omap2evm_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.ops = &omap2evm_ops,
};
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
index fd24a4acd2f5..6aa428e07d86 100644
--- a/sound/soc/omap/omap3beagle.c
+++ b/sound/soc/omap/omap3beagle.c
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link omap3beagle_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.ops = &omap3beagle_ops,
};
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index fe282d4ef422..ad219aaf7cb8 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -228,14 +228,14 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
.name = "PCM1773",
.stream_name = "HiFi Out",
.cpu_dai = &omap_mcbsp_dai[0],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.ops = &omap3pandora_out_ops,
.init = omap3pandora_out_init,
}, {
.name = "TWL4030",
.stream_name = "Line/Mic In",
.cpu_dai = &omap_mcbsp_dai[1],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.ops = &omap3pandora_in_ops,
.init = omap3pandora_in_init,
}
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
index a72dc4e159e5..ec4f8fd8b3a2 100644
--- a/sound/soc/omap/overo.c
+++ b/sound/soc/omap/overo.c
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link overo_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.ops = &overo_ops,
};
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index 10f1c867f11d..1c7974101a0b 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -197,7 +197,7 @@ static struct snd_soc_dai_link sdp3430_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
- .codec_dai = &twl4030_dai,
+ .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
.init = sdp3430_twl4030_init,
.ops = &sdp3430_ops,
};