blob: 8304dad4e6960d112ab8f311d96bd4afe3d99b17 [file] [log] [blame]
/*
* 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
*
*/
#define DEBUG
#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,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
{
.name = "Media Capture1",
.dma_req = OMAP44XX_DMA_ABE_REQ_3,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
},
{
{},
{
.name = "Media Capture2",
.dma_req = OMAP44XX_DMA_ABE_REQ_4,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
},
{
{
.name = "Voice Playback",
.dma_req = OMAP44XX_DMA_ABE_REQ_1,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
{
.name = "Voice Capture",
.dma_req = OMAP44XX_DMA_ABE_REQ_2,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
},
{
{
.name = "Tones Playback",
.dma_req = OMAP44XX_DMA_ABE_REQ_5,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},{},
},
{
{
.name = "Vibra Playback",
.dma_req = OMAP44XX_DMA_ABE_REQ_6,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},{},
},
{
{
.name = "MODEM Playback",
.dma_req = OMAP44XX_DMA_ABE_REQ_1,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
{
.name = "MODEM Capture",
.dma_req = OMAP44XX_DMA_ABE_REQ_2,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.sync_mode = OMAP_DMA_SYNC_PACKET,
},
},
{
{
.name = "Low Power Playback",
.dma_req = OMAP44XX_DMA_ABE_REQ_0,
.data_type = OMAP_DMA_DATA_TYPE_S32,
.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 2 ports */
format.f = 8000;
format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(BT_VX_DL_PORT, &format, MCBSP2_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 2 ports */
format.f = 8000;
format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(BT_VX_UL_PORT, &format, MCBSP2_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 1 ports */
format.f = 48000;
format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(MM_EXT_OUT_PORT, &format, MCBSP1_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 1 ports */
format.f = 48000;
format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(MM_EXT_IN_PORT, &format, MCBSP1_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 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;
dma_data->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;
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_widget mm1_widgets[] = {
{"MM_DL", SND_SOC_DAI_CHAN_STEREO},
{"MM_UL1", SND_SOC_DAI_CHAN_MASK(8)},
};
static struct snd_soc_dai_widget mm2_widgets[] = {
{"MM_UL2", SND_SOC_DAI_CHAN_STEREO},
};
static struct snd_soc_dai_widget voice_widgets[] = {
{"VX_DL", SND_SOC_DAI_CHAN_STEREO},
{"VX_UL", SND_SOC_DAI_CHAN_STEREO},
};
static struct snd_soc_dai_widget tones_widgets[] = {
{"TONES_DL", SND_SOC_DAI_CHAN_STEREO},
};
static struct snd_soc_dai_widget vibra_widgets[] = {
{"VIB_DL", SND_SOC_DAI_CHAN_STEREO},
};
static struct snd_soc_dai_widget mm_lp_widgets[] = {
{"MM_DL_LP", SND_SOC_DAI_CHAN_STEREO},
};
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 | SNDRV_PCM_RATE_44100,
.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,
.widgets = mm1_widgets,
.num_widgets = ARRAY_SIZE(mm1_widgets),
},
{ /* 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,
.widgets = mm2_widgets,
.num_widgets = ARRAY_SIZE(mm2_widgets),
},
{ /* 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,
.widgets = voice_widgets,
.num_widgets = ARRAY_SIZE(voice_widgets),
},
{ /* 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 | SNDRV_PCM_RATE_44100,
.formats = OMAP_ABE_FORMATS,
},
.ops = &omap_abe_dai_ops,
.widgets = tones_widgets,
.num_widgets = ARRAY_SIZE(tones_widgets),
},
{ /* 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,
.widgets = vibra_widgets,
.num_widgets = ARRAY_SIZE(vibra_widgets),
},
{ /* 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,
.widgets = voice_widgets,
.num_widgets = ARRAY_SIZE(voice_widgets),
},
{ /* 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,
.widgets = mm_lp_widgets,
.num_widgets = ARRAY_SIZE(mm_lp_widgets),
},
};
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");