aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap/sdp4430.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/omap/sdp4430.c')
-rw-r--r--sound/soc/omap/sdp4430.c551
1 files changed, 529 insertions, 22 deletions
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index 189e039..3d6a554 100644
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -21,28 +21,90 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-dapm.h>
#include <sound/jack.h>
+#include <sound/soc-dsp.h>
#include <asm/mach-types.h>
#include <plat/hardware.h>
#include <plat/mux.h>
+#include <plat/mcbsp.h>
-#include "mcpdm.h"
+#include "omap-mcpdm.h"
+#include "omap-abe.h"
#include "omap-pcm.h"
+#include "omap-mcbsp.h"
#include "../codecs/twl6040.h"
static int twl6040_power_mode;
+static int mcbsp_cfg;
-static int sdp4430_hw_params(struct snd_pcm_substream *substream,
+static int sdp4430_modem_mcbsp_configure(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, int flag)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_substream *modem_substream[2];
+ struct snd_soc_pcm_runtime *modem_rtd;
+ int channels;
+
+ if (flag) {
+ modem_substream[substream->stream] =
+ snd_soc_get_dai_substream(rtd->card,
+ OMAP_ABE_BE_MM_EXT1,
+ substream->stream);
+ if (unlikely(modem_substream[substream->stream] == NULL))
+ return -ENODEV;
+
+ modem_rtd =
+ modem_substream[substream->stream]->private_data;
+
+ if (!mcbsp_cfg) {
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(modem_rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "can't set Modem cpu DAI configuration\n");
+ goto exit;
+ } else {
+ mcbsp_cfg = 1;
+ }
+ }
+
+ if (params != NULL) {
+ /* Configure McBSP internal buffer usage */
+ /* this need to be done for playback and/or record */
+ channels = params_channels(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ omap_mcbsp_set_rx_threshold(
+ modem_rtd->cpu_dai->id, channels);
+ else
+ omap_mcbsp_set_tx_threshold(
+ modem_rtd->cpu_dai->id, channels);
+ }
+ } else {
+ mcbsp_cfg = 0;
+ }
+
+exit:
+ return ret;
+}
+
+static int sdp4430_mcpdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int clk_id, freq;
- int ret;
+ struct snd_soc_dsp_params *dsp_params;
+ int clk_id, freq, ret, stream = substream->stream;
if (twl6040_power_mode) {
clk_id = TWL6040_SYSCLK_SEL_HPPLL;
@@ -59,13 +121,113 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream,
printk(KERN_ERR "can't set codec system clock\n");
return ret;
}
+ list_for_each_entry(dsp_params, &rtd->dsp[stream].fe_clients, list_fe) {
+
+ if (dsp_params->fe->cpu_dai->id != ABE_FRONTEND_DAI_MODEM)
+ continue;
+
+ /* freed Modem McBSP configuration */
+ ret = sdp4430_modem_mcbsp_configure(substream, params, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set Modem cpu DAI configuration\n");
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int sdp4430_mcpdm_hw_free(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dsp_params *dsp_params;
+ int stream = substream->stream;
+
+ list_for_each_entry(dsp_params, &rtd->dsp[stream].fe_clients, list_fe) {
+
+ if (dsp_params->fe->cpu_dai->id != ABE_FRONTEND_DAI_MODEM)
+ continue;
+
+ /* freed Modem McBSP configuration */
+ ret = sdp4430_modem_mcbsp_configure(substream, NULL, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set Modem cpu DAI configuration\n");
+ return ret;
+ }
+ }
return ret;
}
-static struct snd_soc_ops sdp4430_ops = {
- .hw_params = sdp4430_hw_params,
+static struct snd_soc_ops sdp4430_mcpdm_ops = {
+ .hw_params = sdp4430_mcpdm_hw_params,
+ .hw_free = sdp4430_mcpdm_hw_free,
};
+static int sdp4430_mcbsp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ unsigned int be_id;
+
+ be_id = rtd->dai_link->be_id;
+
+ if (be_id == OMAP_ABE_DAI_MM_FM) {
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ } else if (be_id == OMAP_ABE_DAI_BT_VX) {
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ }
+
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /*
+ * TODO: where does this clock come from (external source??) -
+ * do we need to enable it.
+ */
+ /* Set McBSP clock to external */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK,
+ 64 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu system clock\n");
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops sdp4430_mcbsp_ops = {
+ .hw_params = sdp4430_mcbsp_hw_params,
+};
+
+static int mcbsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ unsigned int be_id = rtd->dai_link->be_id;
+
+ if (be_id == OMAP_ABE_DAI_MM_FM)
+ channels->min = 2;
+ else if (be_id == OMAP_ABE_DAI_BT_VX)
+ channels->min = 1;
+ snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
/* Headset jack */
static struct snd_soc_jack hs_jack;
@@ -175,6 +337,14 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_enable_pin(dapm, "Headset Mic");
snd_soc_dapm_enable_pin(dapm, "Headset Stereophone");
+ /* allow audio paths from the audio modem to run during suspend */
+ snd_soc_dapm_ignore_suspend(dapm, "Ext Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Ext Spk");
+ snd_soc_dapm_ignore_suspend(dapm, "AFML");
+ snd_soc_dapm_ignore_suspend(dapm, "AFMR");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Stereophone");
+
ret = snd_soc_dapm_sync(dapm);
if (ret)
return ret;
@@ -193,37 +363,374 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
else
snd_soc_jack_report(&hs_jack, SND_JACK_HEADSET, SND_JACK_HEADSET);
+ /* wait 500 ms before switching of HS power */
+ rtd->pmdown_time = 500;
+
return ret;
}
+static int sdp4430_twl6040_dl2_init(struct snd_soc_pcm_runtime *rtd)
+{
+ /* wait 500 ms before switching of HF power */
+ rtd->pmdown_time = 500;
+
+ return 0;
+}
+
+/* TODO: make this a separate BT CODEC driver or DUMMY */
+static struct snd_soc_dai_driver dai[] = {
+{
+ .name = "Bluetooth",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_48000,
+ .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 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+/* TODO: make this a separate FM CODEC driver or DUMMY */
+{
+ .name = "FM Digital",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "HDMI",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+};
+
+struct snd_soc_dsp_link fe_media = {
+ .playback = true,
+ .capture = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
+
+struct snd_soc_dsp_link fe_media_capture = {
+ .capture = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
+
+struct snd_soc_dsp_link fe_tones = {
+ .playback = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
+
+struct snd_soc_dsp_link fe_vib = {
+ .playback = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
+
+struct snd_soc_dsp_link fe_modem = {
+ .playback = true,
+ .capture = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
+
+struct snd_soc_dsp_link fe_lp_media = {
+ .playback = true,
+ .trigger =
+ {SND_SOC_DSP_TRIGGER_BESPOKE, SND_SOC_DSP_TRIGGER_BESPOKE},
+};
/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sdp4430_dai = {
- .name = "TWL6040",
- .stream_name = "TWL6040",
- .cpu_dai_name ="omap-mcpdm-dai",
- .codec_dai_name = "twl6040-hifi",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl6040-codec",
- .init = sdp4430_twl6040_init,
- .ops = &sdp4430_ops,
+static struct snd_soc_dai_link sdp4430_dai[] = {
+
+/*
+ * Frontend DAIs - i.e. userspace visible interfaces (ALSA PCMs)
+ */
+
+ {
+ .name = "SDP4430 Media",
+ .stream_name = "Multimedia",
+
+ /* ABE components - MM-UL & MM_DL */
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "omap-pcm-audio",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_media,
+ },
+ {
+ .name = "SDP4430 Media Capture",
+ .stream_name = "Multimedia Capture",
+
+ /* ABE components - MM-UL2 */
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "omap-pcm-audio",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_media_capture,
+ },
+ {
+ .name = "SDP4430 Voice",
+ .stream_name = "Voice",
+
+ /* ABE components - VX-UL & VX-DL */
+ .cpu_dai_name = "Voice",
+ .platform_name = "omap-pcm-audio",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_media,
+ .no_host_mode = SND_SOC_DAI_LINK_OPT_HOST,
+ },
+ {
+ .name = "SDP4430 Tones Playback",
+ .stream_name = "Tone Playback",
+
+ /* ABE components - TONES_DL */
+ .cpu_dai_name = "Tones",
+ .platform_name = "omap-pcm-audio",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_tones,
+ },
+ {
+ .name = "SDP4430 Vibra Playback",
+ .stream_name = "VIB-DL",
+
+ /* ABE components - DMIC UL 2 */
+ .cpu_dai_name = "Vibra",
+ .platform_name = "omap-pcm-audio",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_vib,
+ },
+ {
+ .name = "SDP4430 MODEM",
+ .stream_name = "MODEM",
+
+ /* ABE components - MODEM <-> McBSP2 */
+ .cpu_dai_name = "MODEM",
+ .platform_name = "aess",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_modem,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "SDP4430 Media LP",
+ .stream_name = "Multimedia",
+
+ /* ABE components - MM-DL (mmap) */
+ .cpu_dai_name = "MultiMedia1 LP",
+ .platform_name = "aess",
+
+ .dynamic = 1, /* BE is dynamic */
+ .dsp_link = &fe_lp_media,
+ },
+ {
+ .name = "Legacy McBSP",
+ .stream_name = "Multimedia",
+
+ /* ABE components - MCBSP2 - MM-EXT */
+ .cpu_dai_name = "omap-mcbsp-dai.1",
+ .platform_name = "omap-pcm-audio",
+
+ /* FM */
+ .codec_dai_name = "FM Digital",
+
+ .no_codec = 1, /* TODO: have a dummy CODEC */
+ .ops = &sdp4430_mcbsp_ops,
+ },
+ {
+ .name = "Legacy McPDM",
+ .stream_name = "Headset Playback",
+
+ /* ABE components - DL1 */
+ .cpu_dai_name = "mcpdm-dl",
+ .platform_name = "omap-pcm-audio",
+
+ /* Phoenix - DL1 DAC */
+ .codec_dai_name = "twl6040-dl1",
+ .codec_name = "twl6040-codec",
+
+ .ops = &sdp4430_mcpdm_ops,
+ },
+
+/*
+ * Backend DAIs - i.e. dynamically matched interfaces, invisible to userspace.
+ * Matched to above interfaces at runtime, based upon use case.
+ */
+
+ {
+ .name = OMAP_ABE_BE_PDM_DL1,
+ .stream_name = "HS Playback",
+
+ /* ABE components - DL1 */
+ .cpu_dai_name = "mcpdm-dl1",
+ .platform_name = "aess",
+
+ /* Phoenix - DL1 DAC */
+ .codec_dai_name = "twl6040-dl1",
+ .codec_name = "twl6040-codec",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .init = sdp4430_twl6040_init,
+ .ops = &sdp4430_mcpdm_ops,
+ .be_id = OMAP_ABE_DAI_PDM_DL1,
+ },
+ {
+ .name = OMAP_ABE_BE_PDM_UL1,
+ .stream_name = "Analog Capture",
+
+ /* ABE components - UL1 */
+ .cpu_dai_name = "mcpdm-ul1",
+ .platform_name = "aess",
+
+ /* Phoenix - UL ADC */
+ .codec_dai_name = "twl6040-ul",
+ .codec_name = "twl6040-codec",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .ops = &sdp4430_mcpdm_ops,
+ .be_id = OMAP_ABE_DAI_PDM_UL,
+ },
+ {
+ .name = OMAP_ABE_BE_PDM_DL2,
+ .stream_name = "HF Playback",
+
+ /* ABE components - DL2 */
+ .cpu_dai_name = "mcpdm-dl2",
+ .platform_name = "aess",
+
+ /* Phoenix - DL2 DAC */
+ .codec_dai_name = "twl6040-dl2",
+ .codec_name = "twl6040-codec",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .init = sdp4430_twl6040_dl2_init,
+ .ops = &sdp4430_mcpdm_ops,
+ .be_id = OMAP_ABE_DAI_PDM_DL2,
+ },
+ {
+ .name = OMAP_ABE_BE_PDM_VIB,
+ .stream_name = "Vibra",
+
+ /* ABE components - VIB1 DL */
+ .cpu_dai_name = "mcpdm-vib",
+ .platform_name = "aess",
+
+ /* Phoenix - PDM to PWM */
+ .codec_dai_name = "twl6040-vib",
+ .codec_name = "twl6040-codec",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .ops = &sdp4430_mcpdm_ops,
+ .be_id = OMAP_ABE_DAI_PDM_VIB,
+ },
+ {
+ .name = OMAP_ABE_BE_BT_VX,
+ .stream_name = "BT",
+
+ /* ABE components - MCBSP1 - BT-VX */
+ .cpu_dai_name = "omap-mcbsp-dai.0",
+ .platform_name = "aess",
+
+ /* Bluetooth */
+ .codec_dai_name = "Bluetooth",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .no_codec = 1, /* TODO: have a dummy CODEC */
+ .be_hw_params_fixup = mcbsp_be_hw_params_fixup,
+ .ops = &sdp4430_mcbsp_ops,
+ .be_id = OMAP_ABE_DAI_BT_VX,
+ },
+ {
+ .name = OMAP_ABE_BE_MM_EXT0,
+ .stream_name = "FM",
+
+ /* ABE components - MCBSP2 - MM-EXT */
+ .cpu_dai_name = "omap-mcbsp-dai.1",
+ .platform_name = "aess",
+
+ /* FM */
+ .codec_dai_name = "FM Digital",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .no_codec = 1, /* TODO: have a dummy CODEC */
+ .be_hw_params_fixup = mcbsp_be_hw_params_fixup,
+ .ops = &sdp4430_mcbsp_ops,
+ .be_id = OMAP_ABE_DAI_MM_FM,
+ },
+ {
+ .name = OMAP_ABE_BE_MM_EXT1,
+ .stream_name = "MODEM",
+
+ /* ABE components - MCBSP2 - MM-EXT */
+ .cpu_dai_name = "omap-mcbsp-dai.1",
+ .platform_name = "aess",
+
+ /* MODEM */
+ .codec_dai_name = "MODEM",
+
+ .no_pcm = 1, /* don't create ALSA pcm for this */
+ .no_codec = 1, /* TODO: have a dummy CODEC */
+ .be_hw_params_fixup = mcbsp_be_hw_params_fixup,
+ .ops = &sdp4430_mcbsp_ops,
+ .be_id = OMAP_ABE_DAI_MODEM,
+ .ignore_suspend = 1,
+ },
};
/* Audio machine driver */
static struct snd_soc_card snd_soc_sdp4430 = {
- .name = "SDP4430",
- .dai_link = &sdp4430_dai,
- .num_links = 1,
+ .driver_name = "OMAP4",
+ .long_name = "TI OMAP4 Board",
+ .dai_link = sdp4430_dai,
+ .num_links = ARRAY_SIZE(sdp4430_dai),
};
static struct platform_device *sdp4430_snd_device;
+struct i2c_adapter *adapter;
static int __init sdp4430_soc_init(void)
{
int ret;
- if (!machine_is_omap_4430sdp())
+ if (!machine_is_omap_4430sdp() && !machine_is_omap4_panda()) {
+ pr_debug("Not SDP4430 or PandaBoard!\n");
return -ENODEV;
+ }
printk(KERN_INFO "SDP4430 SoC init\n");
+ if (machine_is_omap_4430sdp())
+ snd_soc_sdp4430.name = "SDP4430";
+ else if (machine_is_omap4_panda())
+ snd_soc_sdp4430.name = "Panda";
sdp4430_snd_device = platform_device_alloc("soc-audio", -1);
if (!sdp4430_snd_device) {
@@ -231,15 +738,15 @@ static int __init sdp4430_soc_init(void)
return -ENOMEM;
}
+ ret = snd_soc_register_dais(&sdp4430_snd_device->dev, dai, ARRAY_SIZE(dai));
+ if (ret < 0)
+ goto err;
platform_set_drvdata(sdp4430_snd_device, &snd_soc_sdp4430);
ret = platform_device_add(sdp4430_snd_device);
if (ret)
goto err;
- /* Codec starts in HP mode */
- twl6040_power_mode = 1;
-
return 0;
err: