diff options
Diffstat (limited to 'sound/soc/omap/omap-abe.c')
-rw-r--r-- | sound/soc/omap/omap-abe.c | 1563 |
1 files changed, 1563 insertions, 0 deletions
diff --git a/sound/soc/omap/omap-abe.c b/sound/soc/omap/omap-abe.c new file mode 100644 index 0000000..da5ba44 --- /dev/null +++ b/sound/soc/omap/omap-abe.c @@ -0,0 +1,1563 @@ +/* + * omap-abe.c -- OMAP ALSA SoC DAI driver using Audio Backend + * + * Copyright (C) 2010 Texas Instruments + * + * Contact: Liam Girdwood <lrg@ti.com> + * Misael Lopez Cruz <misael.lopez@ti.com> + * Sebastien Guiriec <s-guiriec@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/soc-dsp.h> + +#include <plat/dma-44xx.h> +#include <plat/dma.h> +#include "omap-pcm.h" +#include "omap-abe.h" +#include "omap-abe-dsp.h" +#include "abe/abe_main.h" +#include "abe/port_mgr.h" + +#define OMAP_ABE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct omap_abe_data { + /* MODEM FE*/ + struct snd_pcm_substream *modem_substream[2]; + struct snd_soc_dai *modem_dai; + + struct abe *abe; + + /* BE & FE Ports */ + struct omap_abe_port *port[OMAP_ABE_MAX_PORT_ID + 1]; + + int active_dais; + int suspended_dais; +}; + +/* + * Stream DMA parameters + */ +static struct omap_pcm_dma_data omap_abe_dai_dma_params[7][2] = { +{ + { + .name = "Media Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_0, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, + { + .name = "Media Capture1", + .dma_req = OMAP44XX_DMA_ABE_REQ_3, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, +}, +{ + {}, + { + .name = "Media Capture2", + .dma_req = OMAP44XX_DMA_ABE_REQ_4, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, +}, +{ + { + .name = "Voice Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_1, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, + { + .name = "Voice Capture", + .dma_req = OMAP44XX_DMA_ABE_REQ_2, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, +}, +{ + { + .name = "Tones Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_5, + .sync_mode = OMAP_DMA_SYNC_PACKET, + },{}, +}, +{ + { + .name = "Vibra Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_6, + .sync_mode = OMAP_DMA_SYNC_PACKET, + },{}, +}, +{ + { + .name = "MODEM Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_1, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, + { + .name = "MODEM Capture", + .dma_req = OMAP44XX_DMA_ABE_REQ_2, + .sync_mode = OMAP_DMA_SYNC_PACKET, + }, +}, +{ + { + .name = "Low Power Playback", + .dma_req = OMAP44XX_DMA_ABE_REQ_0, + .sync_mode = OMAP_DMA_SYNC_PACKET, + },{}, +},}; + +static int modem_get_dai(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_pcm_runtime *modem_rtd; + + abe_priv->modem_substream[substream->stream] = + snd_soc_get_dai_substream(rtd->card, + OMAP_ABE_BE_MM_EXT1, !substream->stream); + + if (abe_priv->modem_substream[substream->stream] == NULL) + return -ENODEV; + + modem_rtd = abe_priv->modem_substream[substream->stream]->private_data; + abe_priv->modem_substream[substream->stream]->runtime = substream->runtime; + abe_priv->modem_dai = modem_rtd->cpu_dai; + + return 0; +} + +int omap_abe_set_dl1_output(int output) +{ + int gain; + + /* + * the output itself is not important, but the DL1 gain + * to use when each output is active + */ + switch (output) { + case OMAP_ABE_DL1_HEADSET_LP: + gain = GAIN_M8dB; + break; + case OMAP_ABE_DL1_HEADSET_HP: + case OMAP_ABE_DL1_EARPIECE: + gain = GAIN_M1dB; + break; + case OMAP_ABE_DL1_NO_PDM: + gain = GAIN_0dB; + break; + default: + return -EINVAL; + } + + abe_write_gain(GAINS_DL1, gain, RAMP_2MS, GAIN_LEFT_OFFSET); + abe_write_gain(GAINS_DL1, gain, RAMP_2MS, GAIN_RIGHT_OFFSET); + + return 0; +} +EXPORT_SYMBOL(omap_abe_set_dl1_output); + +static int omap_abe_dl1_enabled(struct omap_abe_data *abe_priv) +{ + /* DL1 path is common for PDM_DL1, BT_VX_DL and MM_EXT_DL */ + return omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_PDM_DL1]) + + omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_DL]) + + omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_DL]); +} + +static int omap_abe_dl2_enabled(struct omap_abe_data *abe_priv) +{ + return omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_PDM_DL2]); +} + +static void mute_be(struct snd_soc_pcm_runtime *be, + struct snd_soc_dai *dai, int stream) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(&be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_BT_VX: + case OMAP_ABE_DAI_MM_FM: + /* + * DL1 Mixer->SDT Mixer and DL1 gain are common for + * PDM_DL1, BT_VX_DL and MM_EXT_DL, mute those gains + * only if the last active BE + */ + if (omap_abe_dl1_enabled(abe_priv) == 1) { + abe_mute_gain(GAINS_DL1, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_DL1, GAIN_RIGHT_OFFSET); + abe_mute_gain(MIXSDT, MIX_SDT_INPUT_DL1_MIXER); + } + break; + case OMAP_ABE_DAI_PDM_DL2: + abe_mute_gain(GAINS_DL2, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_DL2, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_PDM_VIB: + case OMAP_ABE_DAI_MODEM: + break; + } + } else { + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_UL: + abe_mute_gain(GAINS_AMIC, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_AMIC, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_BT_VX: + abe_mute_gain(GAINS_BTUL, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_BTUL, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_MM_FM: + case OMAP_ABE_DAI_MODEM: + break; + case OMAP_ABE_DAI_DMIC0: + abe_mute_gain(GAINS_DMIC1, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_DMIC1, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_DMIC1: + abe_mute_gain(GAINS_DMIC2, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_DMIC2, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_DMIC2: + abe_mute_gain(GAINS_DMIC3, GAIN_LEFT_OFFSET); + abe_mute_gain(GAINS_DMIC3, GAIN_RIGHT_OFFSET); + break; + } + } +} + +static void unmute_be(struct snd_soc_pcm_runtime *be, + struct snd_soc_dai *dai, int stream) +{ + dev_dbg(&be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_BT_VX: + case OMAP_ABE_DAI_MM_FM: + /* + * DL1 Mixer->SDT Mixer and DL1 gain are common for + * PDM_DL1, BT_VX_DL and MM_EXT_DL, unmute when any + * of them becomes active + */ + abe_unmute_gain(GAINS_DL1, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_DL1, GAIN_RIGHT_OFFSET); + abe_unmute_gain(MIXSDT, MIX_SDT_INPUT_DL1_MIXER); + break; + case OMAP_ABE_DAI_PDM_DL2: + abe_unmute_gain(GAINS_DL2, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_DL2, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_PDM_VIB: + break; + case OMAP_ABE_DAI_MODEM: + break; + } + } else { + + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_UL: + abe_unmute_gain(GAINS_AMIC, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_AMIC, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_BT_VX: + abe_unmute_gain(GAINS_BTUL, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_BTUL, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_MM_FM: + case OMAP_ABE_DAI_MODEM: + break; + case OMAP_ABE_DAI_DMIC0: + abe_unmute_gain(GAINS_DMIC1, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_DMIC1, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_DMIC1: + abe_unmute_gain(GAINS_DMIC2, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_DMIC2, GAIN_RIGHT_OFFSET); + break; + case OMAP_ABE_DAI_DMIC2: + abe_unmute_gain(GAINS_DMIC3, GAIN_LEFT_OFFSET); + abe_unmute_gain(GAINS_DMIC3, GAIN_RIGHT_OFFSET); + break; + } + } +} + +static void enable_be_port(struct snd_soc_pcm_runtime *be, + struct snd_soc_dai *dai, int stream) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + abe_data_format_t format; + + dev_dbg(&be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream); + + switch (be->dai_link->be_id) { + /* McPDM is a special case, handled by McPDM driver */ + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_PDM_DL2: + case OMAP_ABE_DAI_PDM_VIB: + case OMAP_ABE_DAI_PDM_UL: + break; + case OMAP_ABE_DAI_BT_VX: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + + /* port can only be configured if it's not running */ + if (omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_DL])) + return; + + /* BT_DL connection to McBSP 1 ports */ + format.f = 8000; + format.samp_format = MONO_RSHIFTED_16; + abe_connect_serial_port(BT_VX_DL_PORT, &format, MCBSP1_TX); + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_DL]); + } else { + + /* port can only be configured if it's not running */ + if (omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_UL])) + return; + + /* BT_UL connection to McBSP 1 ports */ + format.f = 8000; + format.samp_format = MONO_RSHIFTED_16; + abe_connect_serial_port(BT_VX_UL_PORT, &format, MCBSP1_RX); + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_UL]); + } + break; + case OMAP_ABE_DAI_MM_FM: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + + /* port can only be configured if it's not running */ + if (omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_DL])) + return; + + /* MM_EXT connection to McBSP 2 ports */ + format.f = 48000; + format.samp_format = STEREO_RSHIFTED_16; + abe_connect_serial_port(MM_EXT_OUT_PORT, &format, MCBSP2_TX); + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_DL]); + } else { + + /* port can only be configured if it's not running */ + if (omap_abe_port_is_enabled(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_UL])) + return; + + /* MM_EXT connection to McBSP 2 ports */ + format.f = 48000; + format.samp_format = STEREO_RSHIFTED_16; + abe_connect_serial_port(MM_EXT_IN_PORT, &format, MCBSP2_RX); + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_UL]); + } + break; + case OMAP_ABE_DAI_DMIC0: + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC0]); + break; + case OMAP_ABE_DAI_DMIC1: + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC1]); + break; + case OMAP_ABE_DAI_DMIC2: + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC2]); + break; + } +} + +static void enable_fe_port(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, dai->name, stream); + + switch(dai->id) { + case ABE_FRONTEND_DAI_MEDIA: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_DL1]); + else + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_UL1]); + break; + case ABE_FRONTEND_DAI_LP_MEDIA: + abe_enable_data_transfer(MM_DL_PORT); + break; + case ABE_FRONTEND_DAI_MEDIA_CAPTURE: + if (stream == SNDRV_PCM_STREAM_CAPTURE) + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_UL2]); + break; + case ABE_FRONTEND_DAI_MODEM: + case ABE_FRONTEND_DAI_VOICE: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VX_DL]); + else + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VX_UL]); + break; + case ABE_FRONTEND_DAI_TONES: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_TONES]); + break; + case ABE_FRONTEND_DAI_VIBRA: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_enable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VIB]); + break; + } +} + +static void disable_be_port(struct snd_soc_pcm_runtime *be, + struct snd_soc_dai *dai, int stream) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(&be->dev, "%s: %s %d\n", __func__, be->cpu_dai->name, stream); + + switch (be->dai_link->be_id) { + /* McPDM is a special case, handled by McPDM driver */ + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_PDM_DL2: + case OMAP_ABE_DAI_PDM_VIB: + case OMAP_ABE_DAI_PDM_UL: + break; + case OMAP_ABE_DAI_BT_VX: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_DL]); + else + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_UL]); + break; + case OMAP_ABE_DAI_MM_FM: + case OMAP_ABE_DAI_MODEM: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_DL]); + else + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_MM_EXT_UL]); + break; + case OMAP_ABE_DAI_DMIC0: + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC0]); + break; + case OMAP_ABE_DAI_DMIC1: + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC1]); + break; + case OMAP_ABE_DAI_DMIC2: + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_BE_PORT_DMIC2]); + break; + } +} + +static void disable_fe_port(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, dai->name, stream); + + switch(dai->id) { + case ABE_FRONTEND_DAI_MEDIA: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_DL1]); + else + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_UL1]); + break; + case ABE_FRONTEND_DAI_LP_MEDIA: + abe_disable_data_transfer(MM_DL_PORT); + break; + case ABE_FRONTEND_DAI_MEDIA_CAPTURE: + if (stream == SNDRV_PCM_STREAM_CAPTURE) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_MM_UL2]); + break; + case ABE_FRONTEND_DAI_MODEM: + case ABE_FRONTEND_DAI_VOICE: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VX_DL]); + else + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VX_UL]); + break; + case ABE_FRONTEND_DAI_TONES: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_TONES]); + break; + case ABE_FRONTEND_DAI_VIBRA: + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_abe_port_disable(abe_priv->abe, + abe_priv->port[OMAP_ABE_FE_PORT_VIB]); + break; + } +} + +static void mute_fe_port_capture(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int mute) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(fe->cpu_dai); + + dev_dbg(&fe->dev, "%s: %s FE %s BE %s\n", + __func__, mute ? "mute" : "unmute", + fe->dai_link->name, be->dai_link->name); + + switch (fe->cpu_dai->id) { + case ABE_FRONTEND_DAI_MEDIA_CAPTURE: + if (omap_abe_dl1_enabled(abe_priv)) { + if (mute) + abe_mute_gain(MIXDL1, MIX_DL1_INPUT_MM_UL2); + else + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_MM_UL2); + } + if (omap_abe_dl2_enabled(abe_priv)) { + if (mute) + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_MM_UL2); + else + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_MM_UL2); + } + break; + case ABE_FRONTEND_DAI_MODEM: + case ABE_FRONTEND_DAI_VOICE: + if (mute) { + abe_mute_gain(MIXSDT, MIX_SDT_INPUT_UP_MIXER); + abe_mute_gain(MIXAUDUL, MIX_AUDUL_INPUT_UPLINK); + } else { + abe_unmute_gain(MIXSDT, MIX_SDT_INPUT_UP_MIXER); + abe_unmute_gain(MIXAUDUL, MIX_AUDUL_INPUT_UPLINK); + } + break; + case ABE_FRONTEND_DAI_MEDIA: + default: + break; + } +} + +static void mute_fe_port_playback(struct snd_soc_pcm_runtime *fe, + struct snd_soc_pcm_runtime *be, int mute) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(fe->cpu_dai); + + dev_dbg(&fe->dev, "%s: %s FE %s BE %s\n", + __func__, mute ? "mute" : "unmute", + fe->dai_link->name, be->dai_link->name); + + switch (fe->cpu_dai->id) { + case ABE_FRONTEND_DAI_MEDIA: + case ABE_FRONTEND_DAI_LP_MEDIA: + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_BT_VX: + case OMAP_ABE_DAI_MM_FM: + if (mute) { + /* mute if last running DL1-related BE */ + if (omap_abe_dl1_enabled(abe_priv) == 1) + abe_mute_gain(MIXDL1, + MIX_DL1_INPUT_MM_DL); + } else { + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_MM_DL); + } + break; + case OMAP_ABE_DAI_PDM_DL2: + if (mute) + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_MM_DL); + else + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_MM_DL); + break; + case OMAP_ABE_DAI_MODEM: + case OMAP_ABE_DAI_PDM_VIB: + default: + break; + } + break; + case ABE_FRONTEND_DAI_VOICE: + case ABE_FRONTEND_DAI_MODEM: + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_BT_VX: + case OMAP_ABE_DAI_MM_FM: + if (mute) { + /* mute if last running DL1-related BE */ + if (omap_abe_dl1_enabled(abe_priv) == 1) + abe_mute_gain(MIXDL1, + MIX_DL1_INPUT_VX_DL); + } else { + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_VX_DL); + } + break; + case OMAP_ABE_DAI_PDM_DL2: + if (mute) + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL); + else + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL); + break; + case OMAP_ABE_DAI_MODEM: + case OMAP_ABE_DAI_PDM_VIB: + default: + break; + } + break; + case ABE_FRONTEND_DAI_TONES: + switch (be->dai_link->be_id) { + case OMAP_ABE_DAI_PDM_DL1: + case OMAP_ABE_DAI_BT_VX: + case OMAP_ABE_DAI_MM_FM: + if (mute) { + /* mute if last running DL1-related BE */ + if (omap_abe_dl1_enabled(abe_priv) == 1) + abe_mute_gain(MIXDL1, + MIX_DL1_INPUT_TONES); + } else{ + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_TONES); + } + break; + case OMAP_ABE_DAI_PDM_DL2: + if (mute) + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_TONES); + else + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_TONES); + break; + case OMAP_ABE_DAI_MODEM: + case OMAP_ABE_DAI_PDM_VIB: + default: + break; + } + break; + case ABE_FRONTEND_DAI_VIBRA: + default: + break; + } +} + +static void mute_fe_port(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dsp_params *dsp_params; + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, dai->name, stream); + + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mute_fe_port_playback(fe, be, 1); + else + mute_fe_port_capture(fe, be, 1); + } +} + +static void unmute_fe_port(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dsp_params *dsp_params; + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, dai->name, stream); + + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mute_fe_port_playback(fe, be, 0); + else + mute_fe_port_capture(fe, be, 0); + } +} + +static void capture_trigger(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int cmd) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dsp_params *dsp_params; + struct snd_pcm_substream *be_substream; + int stream = substream->stream; + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, fe->cpu_dai->name, stream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + + /* mute and enable BE ports */ + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + /* does this trigger() apply to this BE and stream ? */ + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if ((be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) && + (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP)) + continue; + + be_substream = snd_soc_dsp_get_substream(be, stream); + + /* mute the BE port */ + mute_be(be, dai, stream); + + /* enable the BE port */ + enable_be_port(be, dai, stream); + + /* DAI work must be started/stopped at least 250us after ABE */ + udelay(250); + + /* trigger the BE port */ + snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai); + + be->dsp[stream].state = SND_SOC_DSP_STATE_START; + } + + /* does this trigger() apply to the FE ? */ + if (snd_soc_dsp_is_trigger_for_fe(fe, stream)) { + /* Enable Frontend sDMA */ + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + enable_fe_port(substream, dai, stream); + + /* unmute FE port */ + unmute_fe_port(substream, dai, stream); + } + + /* Restore ABE GAINS AMIC */ + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + /* does this trigger() apply to this BE and stream ? */ + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + /* unmute this BE port */ + unmute_be(be, dai, stream); + } + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Enable sDMA */ + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + enable_fe_port(substream, dai, stream); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* Disable sDMA */ + disable_fe_port(substream, dai, stream); + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + break; + case SNDRV_PCM_TRIGGER_STOP: + /* does this trigger() apply to the FE ? */ + if (snd_soc_dsp_is_trigger_for_fe(fe, stream)) { + /* mute FE port */ + mute_fe_port(substream, dai, stream); + + /* Disable sDMA */ + disable_fe_port(substream, dai, stream); + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + } + + /* disable BE ports */ + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + /* does this trigger() apply to this BE and stream ? */ + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if (be->dsp[stream].state != SND_SOC_DSP_STATE_START) + continue; + + /* only stop if last running user */ + if (soc_dsp_fe_state_count(be, stream, + SND_SOC_DSP_STATE_START) > 1) + continue; + + be_substream = snd_soc_dsp_get_substream(be, stream); + + /* mute the BE port */ + mute_be(be, dai, stream); + + /* disable the BE port */ + disable_be_port(be, dai, stream); + + /* DAI work must be started/stopped at least 250us after ABE */ + udelay(250); + + /* trigger BE port */ + snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai); + + be->dsp[stream].state = SND_SOC_DSP_STATE_STOP; + } + break; + default: + break; + } +} + +static void playback_trigger(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int cmd) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dsp_params *dsp_params; + struct snd_pcm_substream *be_substream; + int stream = substream->stream; + + dev_dbg(&fe->dev, "%s: %s %d\n", __func__, fe->cpu_dai->name, stream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + + /* mute and enable ports */ + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + /* does this trigger() apply to the FE ? */ + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if ((be->dsp[stream].state != SND_SOC_DSP_STATE_PREPARE) && + (be->dsp[stream].state != SND_SOC_DSP_STATE_STOP)) + continue; + + be_substream = snd_soc_dsp_get_substream(be, stream); + + /* mute BE port */ + mute_be(be, dai, stream); + + /* enabled BE port */ + enable_be_port(be, dai, stream); + + /* DAI work must be started/stopped at least 250us after ABE */ + udelay(250); + + /* trigger BE port */ + snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai); + + /* unmute the BE port */ + unmute_be(be, dai, stream); + + be->dsp[stream].state = SND_SOC_DSP_STATE_START; + } + + /* does this trigger() apply to the FE ? */ + if (snd_soc_dsp_is_trigger_for_fe(fe, stream)) { + /* Enable Frontend sDMA */ + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + enable_fe_port(substream, dai, stream); + } + + /* unmute FE port (sensitive to runtime udpates) */ + unmute_fe_port(substream, dai, stream); + + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Enable Frontend sDMA */ + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + enable_fe_port(substream, dai, stream); + + /* unmute FE port */ + unmute_fe_port(substream, dai, stream); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* mute FE port */ + mute_fe_port(substream, dai, stream); + + /* disable Frontend sDMA */ + disable_fe_port(substream, dai, stream); + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + + /* mute FE port (sensitive to runtime udpates) */ + mute_fe_port(substream, dai, stream); + + /* does this trigger() apply to the FE ? */ + if (snd_soc_dsp_is_trigger_for_fe(fe, stream)) { + /* disable the transfer */ + disable_fe_port(substream, dai, stream); + snd_soc_dsp_platform_trigger(substream, cmd, fe->platform); + } + + /* disable BE ports */ + list_for_each_entry(dsp_params, &fe->dsp[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dsp_params->be; + + /* does this trigger() apply to this BE and stream ? */ + if (!snd_soc_dsp_is_op_for_be(fe, be, stream)) + continue; + + if (be->dsp[stream].state != SND_SOC_DSP_STATE_START) + continue; + + /* only stop if last running user */ + if (soc_dsp_fe_state_count(be, stream, + SND_SOC_DSP_STATE_START) > 1) + continue; + + be_substream = snd_soc_dsp_get_substream(be, stream); + + /* mute the BE port */ + mute_be(be, dai, stream); + + /* disable the BE */ + disable_be_port(be, dai, stream); + + /* DAI work must be started/stopped at least 250us after ABE */ + udelay(250); + + /* trigger the BE port */ + snd_soc_dai_trigger(be_substream, cmd, be->cpu_dai); + + be->dsp[stream].state = SND_SOC_DSP_STATE_STOP; + } + break; + default: + break; + } +} + +static int omap_abe_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + abe_priv->active_dais++; + + abe_dsp_pm_get(); + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + + ret = modem_get_dai(substream, dai); + if (ret < 0) { + dev_err(dai->dev, "failed to get MODEM DAI\n"); + return ret; + } + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d\n", + __func__, substream->stream); + + ret = snd_soc_dai_startup(abe_priv->modem_substream[substream->stream], + abe_priv->modem_dai); + if (ret < 0) { + dev_err(abe_priv->modem_dai->dev, "failed to open DAI %d\n", ret); + return ret; + } + } + + return ret; +} + +static int omap_abe_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + struct omap_pcm_dma_data *dma_data; + abe_data_format_t format; + abe_dma_t dma_sink; + abe_dma_t dma_params; + int data_type = OMAP_DMA_DATA_TYPE_S32; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + dma_data = &omap_abe_dai_dma_params[dai->id][substream->stream]; + + switch (params_channels(params)) { + case 1: + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) { + format.samp_format = MONO_RSHIFTED_16; + data_type = OMAP_DMA_DATA_TYPE_S16; + } else { + format.samp_format = MONO_MSB; + } + break; + case 2: + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + format.samp_format = STEREO_16_16; + else + format.samp_format = STEREO_MSB; + break; + case 3: + format.samp_format = THREE_MSB; + break; + case 4: + format.samp_format = FOUR_MSB; + break; + case 5: + format.samp_format = FIVE_MSB; + break; + case 6 : + format.samp_format = SIX_MSB; + break; + case 7 : + format.samp_format = SEVEN_MSB; + break; + case 8: + format.samp_format = EIGHT_MSB; + break; + default: + dev_err(dai->dev, "%d channels not supported", + params_channels(params)); + return -EINVAL; + } + + format.f = params_rate(params); + + switch (dai->id) { + case ABE_FRONTEND_DAI_MEDIA: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + abe_connect_cbpr_dmareq_port(MM_DL_PORT, &format, ABE_CBPR0_IDX, + &dma_sink); + abe_read_port_address(MM_DL_PORT, &dma_params); + } else { + abe_connect_cbpr_dmareq_port(MM_UL_PORT, &format, ABE_CBPR3_IDX, + &dma_sink); + abe_read_port_address(MM_UL_PORT, &dma_params); + } + break; + case ABE_FRONTEND_DAI_LP_MEDIA: + return 0; + break; + case ABE_FRONTEND_DAI_MEDIA_CAPTURE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + else { + abe_connect_cbpr_dmareq_port(MM_UL2_PORT, &format, ABE_CBPR4_IDX, + &dma_sink); + abe_read_port_address(MM_UL2_PORT, &dma_params); + } + break; + case ABE_FRONTEND_DAI_VOICE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + abe_connect_cbpr_dmareq_port(VX_DL_PORT, &format, ABE_CBPR1_IDX, + &dma_sink); + abe_read_port_address(VX_DL_PORT, &dma_params); + } else { + abe_connect_cbpr_dmareq_port(VX_UL_PORT, &format, ABE_CBPR2_IDX, + &dma_sink); + abe_read_port_address(VX_UL_PORT, &dma_params); + } + break; + case ABE_FRONTEND_DAI_TONES: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + abe_connect_cbpr_dmareq_port(TONES_DL_PORT, &format, ABE_CBPR5_IDX, + &dma_sink); + abe_read_port_address(TONES_DL_PORT, &dma_params); + } else + return -EINVAL; + break; + case ABE_FRONTEND_DAI_VIBRA: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + abe_connect_cbpr_dmareq_port(VIB_DL_PORT, &format, ABE_CBPR6_IDX, + &dma_sink); + abe_read_port_address(VIB_DL_PORT, &dma_params); + } else + return -EINVAL; + break; + case ABE_FRONTEND_DAI_MODEM: + /* MODEM is special case where data IO is performed by McBSP2 + * directly onto VX_DL and VX_UL (instead of SDMA). + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* Vx_DL connection to McBSP 2 ports */ + format.samp_format = STEREO_RSHIFTED_16; + abe_connect_serial_port(VX_DL_PORT, &format, MCBSP2_RX); + abe_read_port_address(VX_DL_PORT, &dma_params); + } else { + /* Vx_UL connection to McBSP 2 ports */ + format.samp_format = STEREO_RSHIFTED_16; + abe_connect_serial_port(VX_UL_PORT, &format, MCBSP2_TX); + abe_read_port_address(VX_UL_PORT, &dma_params); + } + break; + } + + /* configure frontend SDMA data */ + dma_data->port_addr = (unsigned long)dma_params.data; + dma_data->packet_size = dma_params.iter; + dma_data->data_type = data_type; + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + /* call hw_params on McBSP with correct DMA data */ + snd_soc_dai_set_dma_data(abe_priv->modem_dai, substream, + dma_data); + + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d\n", + __func__, substream->stream); + + ret = snd_soc_dai_hw_params(abe_priv->modem_substream[substream->stream], + params, abe_priv->modem_dai); + if (ret < 0) + dev_err(abe_priv->modem_dai->dev, "MODEM hw_params failed\n"); + return ret; + } + + snd_soc_dai_set_dma_data(dai, substream, dma_data); + + return 0; +} + +static int omap_abe_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + ret = snd_soc_dai_prepare(abe_priv->modem_substream[substream->stream], + abe_priv->modem_dai); + + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d\n", + __func__, substream->stream); + + if (ret < 0) { + dev_err(abe_priv->modem_dai->dev, "MODEM prepare failed\n"); + return ret; + } + } + return ret; +} + +static int omap_abe_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s: %s cmd %d\n", __func__, dai->name, cmd); + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d cmd %d\n", + __func__, substream->stream, cmd); + + ret = snd_soc_dai_trigger(abe_priv->modem_substream[substream->stream], + cmd, abe_priv->modem_dai); + if (ret < 0) { + dev_err(abe_priv->modem_dai->dev, "MODEM trigger failed\n"); + return ret; + } + } + + return ret; +} + +static int omap_abe_dai_bespoke_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s: %s cmd %d\n", __func__, dai->name, cmd); + + if ((dai->id == ABE_FRONTEND_DAI_MODEM) && + snd_soc_dsp_is_trigger_for_fe(fe, substream->stream)) { + + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d cmd %d\n", + __func__, substream->stream, cmd); + + ret = snd_soc_dai_trigger(abe_priv->modem_substream[substream->stream], + cmd, abe_priv->modem_dai); + if (ret < 0) { + dev_err(abe_priv->modem_dai->dev, "MODEM trigger failed\n"); + return ret; + } + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + playback_trigger(substream, dai, cmd); + else + capture_trigger(substream, dai, cmd); + + return ret; +} + +static int omap_abe_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d\n", + __func__, substream->stream); + + ret = snd_soc_dai_hw_free(abe_priv->modem_substream[substream->stream], + abe_priv->modem_dai); + if (ret < 0) { + dev_err(abe_priv->modem_dai->dev, "MODEM hw_free failed\n"); + return ret; + } + } + return ret; +} + +static void omap_abe_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + if (dai->id == ABE_FRONTEND_DAI_MODEM) { + dev_dbg(abe_priv->modem_dai->dev, "%s: MODEM stream %d\n", + __func__, substream->stream); + + snd_soc_dai_shutdown(abe_priv->modem_substream[substream->stream], + abe_priv->modem_dai); + } + + abe_dsp_shutdown(); + abe_dsp_pm_put(); + + abe_priv->active_dais--; +} + +#ifdef CONFIG_PM +static int omap_abe_dai_suspend(struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s: %s active %d\n", + __func__, dai->name, dai->active); + + if (!dai->active) + return 0; + + if (++abe_priv->suspended_dais < abe_priv->active_dais) + return 0; + + abe_mute_gain(MIXSDT, MIX_SDT_INPUT_UP_MIXER); + abe_mute_gain(MIXSDT, MIX_SDT_INPUT_DL1_MIXER); + abe_mute_gain(MIXAUDUL, MIX_AUDUL_INPUT_MM_DL); + abe_mute_gain(MIXAUDUL, MIX_AUDUL_INPUT_TONES); + abe_mute_gain(MIXAUDUL, MIX_AUDUL_INPUT_UPLINK); + abe_mute_gain(MIXAUDUL, MIX_AUDUL_INPUT_VX_DL); + abe_mute_gain(MIXVXREC, MIX_VXREC_INPUT_TONES); + abe_mute_gain(MIXVXREC, MIX_VXREC_INPUT_VX_DL); + abe_mute_gain(MIXVXREC, MIX_VXREC_INPUT_MM_DL); + abe_mute_gain(MIXVXREC, MIX_VXREC_INPUT_VX_UL); + abe_mute_gain(MIXDL1, MIX_DL1_INPUT_MM_DL); + abe_mute_gain(MIXDL1, MIX_DL1_INPUT_MM_UL2); + abe_mute_gain(MIXDL1, MIX_DL1_INPUT_VX_DL); + abe_mute_gain(MIXDL1, MIX_DL1_INPUT_TONES); + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_TONES); + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL); + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_MM_DL); + abe_mute_gain(MIXDL2, MIX_DL2_INPUT_MM_UL2); + abe_mute_gain(MIXECHO, MIX_ECHO_DL1); + abe_mute_gain(MIXECHO, MIX_ECHO_DL2); + + return 0; +} + +static int omap_abe_dai_resume(struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s: %s active %d\n", + __func__, dai->name, dai->active); + + if (!dai->active) + return 0; + + if (abe_priv->suspended_dais-- < abe_priv->active_dais) + return 0; + + abe_unmute_gain(MIXSDT, MIX_SDT_INPUT_UP_MIXER); + abe_unmute_gain(MIXSDT, MIX_SDT_INPUT_DL1_MIXER); + abe_unmute_gain(MIXAUDUL, MIX_AUDUL_INPUT_MM_DL); + abe_unmute_gain(MIXAUDUL, MIX_AUDUL_INPUT_TONES); + abe_unmute_gain(MIXAUDUL, MIX_AUDUL_INPUT_UPLINK); + abe_unmute_gain(MIXAUDUL, MIX_AUDUL_INPUT_VX_DL); + abe_unmute_gain(MIXVXREC, MIX_VXREC_INPUT_TONES); + abe_unmute_gain(MIXVXREC, MIX_VXREC_INPUT_VX_DL); + abe_unmute_gain(MIXVXREC, MIX_VXREC_INPUT_MM_DL); + abe_unmute_gain(MIXVXREC, MIX_VXREC_INPUT_VX_UL); + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_MM_DL); + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_MM_UL2); + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_VX_DL); + abe_unmute_gain(MIXDL1, MIX_DL1_INPUT_TONES); + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_TONES); + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_VX_DL); + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_MM_DL); + abe_unmute_gain(MIXDL2, MIX_DL2_INPUT_MM_UL2); + abe_unmute_gain(MIXECHO, MIX_ECHO_DL1); + abe_unmute_gain(MIXECHO, MIX_ECHO_DL2); + + return 0; +} +#else +#define omap_abe_dai_suspend NULL +#define omap_abe_dai_resume NULL +#endif + +static int omap_abe_dai_probe(struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv; + int i; + + abe_priv = kzalloc(sizeof(struct omap_abe_data), GFP_KERNEL); + if (abe_priv == NULL) + return -ENOMEM; + + abe_priv->abe = omap_abe_port_mgr_get(); + if (!abe_priv->abe) + goto err; + + for (i = 0; i <= OMAP_ABE_MAX_PORT_ID; i++) { + + abe_priv->port[i] = omap_abe_port_open(abe_priv->abe, i); + if (abe_priv->port[i] == NULL) { + for (--i; i >= 0; i--) + omap_abe_port_close(abe_priv->abe, abe_priv->port[i]); + + goto err_port; + } + } + + snd_soc_dai_set_drvdata(dai, abe_priv); + return 0; + +err_port: + omap_abe_port_mgr_put(abe_priv->abe); +err: + kfree(abe_priv); + return -ENOMEM; +} + +static int omap_abe_dai_remove(struct snd_soc_dai *dai) +{ + struct omap_abe_data *abe_priv = snd_soc_dai_get_drvdata(dai); + + omap_abe_port_mgr_put(abe_priv->abe); + kfree(abe_priv); + return 0; +} + +static struct snd_soc_dai_ops omap_abe_dai_ops = { + .startup = omap_abe_dai_startup, + .shutdown = omap_abe_dai_shutdown, + .hw_params = omap_abe_dai_hw_params, + .hw_free = omap_abe_dai_hw_free, + .prepare = omap_abe_dai_prepare, + .trigger = omap_abe_dai_trigger, + .bespoke_trigger = omap_abe_dai_bespoke_trigger, +}; + +static struct snd_soc_dai_driver omap_abe_dai[] = { + { /* Multimedia Playback and Capture */ + .name = "MultiMedia1", + .probe = omap_abe_dai_probe, + .remove = omap_abe_dai_remove, + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "MultiMedia1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = OMAP_ABE_FORMATS, + }, + .capture = { + .stream_name = "MultiMedia1 Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* Multimedia Capture */ + .name = "MultiMedia2", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .capture = { + .stream_name = "MultiMedia2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* Voice Playback and Capture */ + .name = "Voice", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = OMAP_ABE_FORMATS, + }, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* Tones Playback */ + .name = "Tones", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "Tones Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* Vibra */ + .name = "Vibra", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "Vibra Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* MODEM Voice Playback and Capture */ + .name = "MODEM", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "MODEM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = OMAP_ABE_FORMATS, + }, + .capture = { + .stream_name = "MODEM Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, + { /* Low Power HiFi Playback */ + .name = "MultiMedia1 LP", + .suspend = omap_abe_dai_suspend, + .resume = omap_abe_dai_resume, + .playback = { + .stream_name = "MultiMedia1 LP Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = OMAP_ABE_FORMATS, + }, + .ops = &omap_abe_dai_ops, + }, +}; + +static int __devinit omap_abe_probe(struct platform_device *pdev) +{ + return snd_soc_register_dais(&pdev->dev, omap_abe_dai, + ARRAY_SIZE(omap_abe_dai)); +} + +static int __devexit omap_abe_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(omap_abe_dai)); + return 0; +} + +static struct platform_driver omap_abe_driver = { + .driver = { + .name = "omap-abe-dai", + .owner = THIS_MODULE, + }, + .probe = omap_abe_probe, + .remove = __devexit_p(omap_abe_remove), +}; + +static int __init omap_abe_init(void) +{ + return platform_driver_register(&omap_abe_driver); +} +module_init(omap_abe_init); + +static void __exit omap_abe_exit(void) +{ + platform_driver_unregister(&omap_abe_driver); +} +module_exit(omap_abe_exit); + +MODULE_AUTHOR("Liam Girdwood <lrg@ti.com>"); +MODULE_DESCRIPTION("OMAP ABE SoC Interface"); +MODULE_LICENSE("GPL"); |