summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorSebastien Guiriec <s-guiriec@ti.com>2011-05-31 09:23:01 +0100
committerAndy Green <andy.green@linaro.org>2011-05-31 11:04:31 +0100
commit3ec15d87d48a6f976aa4fd22e827e59347f61e3c (patch)
tree81dd48b0ac98c608091945ec9e6c4d1482a10384 /sound
parent3a75f40b089392a6b00cc9f7dd33eb10c4a23294 (diff)
ASoC: McPDM: Add McPDM DAIs with ABE compatibility
When McPDM interface is used with ABE active, there are some interdepencies between them for appropriate enable and disable sequences than cannot expressed properly in ASoC terms. For that reason, a new set of DAIs is created for McPDM + ABE link. McPDM shutdown work is queued in the kernel global queue to ensure proper sequencing and avoid race conditions. Delay time must be longer than power down time specified through ASoC framework. For McPDM DL the ATC channel of ABE is stopped after the AESS close. Enable/Disable of PDM DL ATC should use PM interface to avoid PM issue Change-Id: Ie2aae98a512260f9f8fb01cfdd22b9cea3f644a3 Signed-off-by: Sebastien Guiriec <s-guiriec@ti.com> Signed-off-by: Ricardo Neri <ricardo.neri@ti.com> Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/omap/omap-mcpdm.c169
1 files changed, 166 insertions, 3 deletions
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index 74be28126d5..323606a4cab 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -48,6 +48,10 @@
#include <plat/omap_hwmod.h>
#include "omap-mcpdm.h"
#include "omap-pcm.h"
+#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
+#include "omap-abe-dsp.h"
+#include "abe/abe_main.h"
+#endif
struct omap_mcpdm_data {
struct omap_mcpdm_link *links;
@@ -60,6 +64,9 @@ struct omap_mcpdm {
u8 free;
int irq;
struct delayed_work delayed_work;
+#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
+ struct delayed_work delayed_abe_work;
+#endif
spinlock_t lock;
struct omap_mcpdm_platform_data *pdata;
@@ -621,10 +628,142 @@ static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
.trigger = omap_mcpdm_dai_trigger,
};
+#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
+static int omap_mcpdm_abe_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* make sure we stop any pre-existing shutdown */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ cancel_delayed_work(&mcpdm->delayed_abe_work);
+ }
+
+ ret = omap_mcpdm_dai_startup(substream, dai);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcpdm->dl_active++;
+ else
+ mcpdm->ul_active++;
+
+ return ret;
+}
+
+static void omap_mcpdm_abe_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dai->dev, "%s: active %d\n", __func__, dai->active);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcpdm->dl_active--;
+ else
+ mcpdm->ul_active--;
+
+ if (!dai->active) {
+ if (!mcpdm->ul_active && substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ omap_mcpdm_capture_close(mcpdm, mcpdm->uplink);
+ if (!mcpdm->free && !mcpdm->dn_channels &&
+ !mcpdm->dl_active)
+ omap_mcpdm_free(mcpdm);
+ }
+ if (!mcpdm->dl_active && substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ schedule_delayed_work(&mcpdm->delayed_abe_work,
+ msecs_to_jiffies(1000)); /* TODO: pdata ? */
+ }
+
+}
+
+/* work to delay McPDM shutdown */
+static void playback_abe_work(struct work_struct *work)
+{
+ struct omap_mcpdm *mcpdm =
+ container_of(work, struct omap_mcpdm, delayed_abe_work.work);
+
+ spin_lock(&mcpdm->lock);
+ if (!mcpdm->dl_active && mcpdm->dn_channels) {
+ abe_disable_data_transfer(PDM_DL_PORT);
+ udelay(250);
+ omap_mcpdm_stop(mcpdm, SNDRV_PCM_STREAM_PLAYBACK);
+ omap_mcpdm_playback_close(mcpdm, mcpdm->downlink);
+ abe_dsp_mcpdm_shutdown();
+ }
+ spin_unlock(&mcpdm->lock);
+ abe_dsp_pm_put();
+
+ if (!mcpdm->free && !mcpdm->ul_active)
+ omap_mcpdm_free(mcpdm);
+
+}
+
+static int omap_mcpdm_abe_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ int stream = substream->stream;
+ int ret = 0;
+
+ snd_soc_dai_set_dma_data(dai, substream,
+ &omap_mcpdm_dai_dma_params[stream]);
+
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Check if McPDM is already started */
+ if (!mcpdm->dn_channels) {
+ abe_dsp_pm_get();
+ spin_lock(&mcpdm->lock);
+ /* start ATC before McPDM IP */
+ abe_enable_data_transfer(PDM_DL_PORT);
+ udelay(250);
+ mcpdm->downlink->channels = (PDM_DN_MASK | PDM_CMD_MASK);
+
+ ret = omap_mcpdm_playback_open(mcpdm, &omap_mcpdm_links[0]);
+ if (ret < 0) {
+ spin_unlock(&mcpdm->lock);
+ goto out;
+ }
+
+ omap_mcpdm_start(mcpdm, stream);
+ spin_unlock(&mcpdm->lock);
+ }
+ } else {
+ mcpdm->uplink->channels = PDM_UP1_EN | PDM_UP2_EN;
+ ret = omap_mcpdm_capture_open(mcpdm, &omap_mcpdm_links[1]);
+ }
+
+out:
+ return ret;
+}
+
+
+static int omap_mcpdm_abe_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ omap_mcpdm_dai_trigger(substream, cmd, dai);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops omap_mcpdm_abe_dai_ops = {
+ .startup = omap_mcpdm_abe_dai_startup,
+ .shutdown = omap_mcpdm_abe_dai_shutdown,
+ .hw_params = omap_mcpdm_abe_dai_hw_params,
+ .trigger = omap_mcpdm_abe_dai_trigger,
+};
+#endif
+
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
+#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
{
.name = "mcpdm-dl1",
.playback = {
@@ -633,7 +772,7 @@ static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
- .ops = &omap_mcpdm_dai_ops,
+ .ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-dl2",
@@ -643,7 +782,7 @@ static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
- .ops = &omap_mcpdm_dai_ops,
+ .ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-vib",
@@ -653,7 +792,7 @@ static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
- .ops = &omap_mcpdm_dai_ops,
+ .ops = &omap_mcpdm_abe_dai_ops,
},
{
.name = "mcpdm-ul1",
@@ -663,6 +802,27 @@ static struct snd_soc_dai_driver omap_mcpdm_dai[] = {
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
},
+ .ops = &omap_mcpdm_abe_dai_ops,
+},
+#endif
+{
+ .name = "mcpdm-dl",
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = OMAP_MCPDM_RATES,
+ .formats = OMAP_MCPDM_FORMATS,
+ },
+ .ops = &omap_mcpdm_dai_ops,
+},
+{
+ .name = "mcpdm-ul",
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = OMAP_MCPDM_RATES,
+ .formats = OMAP_MCPDM_FORMATS,
+ },
.ops = &omap_mcpdm_dai_ops,
}, };
@@ -710,6 +870,9 @@ static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
mcpdm->dl2_offset = 0x1F;
INIT_DELAYED_WORK(&mcpdm->delayed_work, playback_work);
+#ifdef CONFIG_SND_OMAP_SOC_ABE_DSP
+ INIT_DELAYED_WORK(&mcpdm->delayed_abe_work, playback_abe_work);
+#endif
ret = snd_soc_register_dais(&pdev->dev, omap_mcpdm_dai,
ARRAY_SIZE(omap_mcpdm_dai));