summaryrefslogtreecommitdiff
path: root/sound/soc/imx
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/imx')
-rw-r--r--sound/soc/imx/Kconfig85
-rw-r--r--sound/soc/imx/Makefile28
-rw-r--r--sound/soc/imx/imx-3stack-ak4647.c445
-rw-r--r--sound/soc/imx/imx-3stack-ak5702.c226
-rw-r--r--sound/soc/imx/imx-3stack-bt.c256
-rw-r--r--sound/soc/imx/imx-3stack-bt.h21
-rw-r--r--sound/soc/imx/imx-3stack-cs42888.c428
-rw-r--r--sound/soc/imx/imx-3stack-sgtl5000.c706
-rw-r--r--sound/soc/imx/imx-3stack-wm8350.c696
-rw-r--r--sound/soc/imx/imx-3stack-wm8580.c437
-rw-r--r--sound/soc/imx/imx-ac97.c564
-rw-r--r--sound/soc/imx/imx-esai.c711
-rw-r--r--sound/soc/imx/imx-esai.h314
-rw-r--r--sound/soc/imx/imx-pcm.c719
-rw-r--r--sound/soc/imx/imx-pcm.h83
-rw-r--r--sound/soc/imx/imx-ssi.c1102
-rw-r--r--sound/soc/imx/imx-ssi.h443
17 files changed, 6504 insertions, 760 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index 252defea93b5..45033c2ecbdf 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -1,15 +1,15 @@
-config SND_IMX_SOC
+config SND_MXC_SOC
tristate "SoC Audio for Freescale i.MX CPUs"
depends on ARCH_MXC
select SND_PCM
- select FIQ
- select SND_SOC_AC97_BUS
help
Say Y or M if you want to add support for codecs attached to
the i.MX SSI interface.
+if SND_MXC_SOC
+
config SND_MXC_SOC_SSI
- tristate
+ tristate
config SND_MXC_SOC_WM1133_EV1
tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
@@ -28,3 +28,80 @@ config SND_SOC_PHYCORE_AC97
help
Say Y if you want to add support for SoC audio on Phytec phyCORE
and phyCARD boards in AC97 mode
+
+config SND_MXC_SOC_ESAI
+ tristate
+
+config SND_MXC_SOC_AC97
+ tristate
+
+config SND_MXC_SOC_IRAM
+ bool "Locate Audio DMA playback buffers in IRAM"
+ help
+ Say Y if you don't want Audio playback buffers in external ram
+
+config SND_SOC_IMX_3STACK_WM8350
+ tristate "SoC Audio support for IMX - WM8350"
+ depends on MFD_WM8350
+ select SND_MXC_SOC_SSI
+ select SND_SOC_WM8350
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the WM8350.
+
+config SND_SOC_IMX_3STACK_SGTL5000
+ tristate "SoC Audio support for IMX - SGTL5000"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_SGTL5000
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the SGTL5000.
+
+config SND_SOC_IMX_3STACK_AK4647
+ tristate "SoC Audio support for IMX - AK4647"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_AK4647
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the AK4647.
+
+config SND_SOC_IMX_3STACK_WM8580
+ tristate "SoC Audio support for IMX - WM8580"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_WM8580
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the WM8580
+
+config SND_SOC_IMX_3STACK_AK5702
+ tristate "SoC Audio support for IMX - AK5702"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_AK5702
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the AK5702
+
+config SND_SOC_IMX_3STACK_BLUETOOTH
+ tristate "SoC Audio support for IMX - BLUETOOTH"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_BLUETOOTH
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the BLUETOOTH
+
+config SND_SOC_IMX_3STACK_CS42888
+ tristate "SoC Audio support for IMX - CS42888"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_CS42888
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the CS42888
+
+config MXC_SSI_DUAL_FIFO
+ bool "MXC SSI enable dual fifo"
+ def_bool n
+ depends on SND_MXC_SOC_SSI && (ARCH_MX51 || ARCH_MX53)
+ help
+ Say Y if you want to use 2 ssi fifo for audio transfer,
+ NOTE: ONLY support MX51 and MX53.
+endif
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index 2d203635ac11..657777f6dd2f 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -1,15 +1,37 @@
# i.MX Platform Support
-snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o
+snd-soc-imx-objs := imx-pcm.o
+snd-soc-imx-ssi-objs := imx-ssi.o
+snd-soc-imx-esai-objs := imx-esai.o
ifdef CONFIG_MACH_MX27
snd-soc-imx-objs += imx-pcm-dma-mx2.o
endif
-obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
-
# i.MX Machine Support
snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
+
+obj-$(CONFIG_SND_MXC_SOC) += snd-soc-imx.o
+obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-imx-ssi.o
+obj-$(CONFIG_SND_MXC_SOC_ESAI) += snd-soc-imx-esai.o
+obj-$(CONFIG_SND_MXC_SOC_AC97) += snd-soc-imx-ac97.o
+
+# i.MX Machine Support
+snd-soc-imx-3stack-wm8350-objs := imx-3stack-wm8350.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8350) += snd-soc-imx-3stack-wm8350.o
+snd-soc-imx-3stack-sgtl5000-objs := imx-3stack-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) += snd-soc-imx-3stack-sgtl5000.o
+snd-soc-imx-3stack-ak4647-objs := imx-3stack-ak4647.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_AK4647) += snd-soc-imx-3stack-ak4647.o
+snd-soc-imx-3stack-wm8580-objs := imx-3stack-wm8580.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8580) += snd-soc-imx-3stack-wm8580.o
+snd-soc-imx-3stack-ak5702-objs := imx-3stack-ak5702.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_AK5702) += snd-soc-imx-3stack-ak5702.o
+snd-soc-imx-3stack-bt-objs := imx-3stack-bt.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH) += snd-soc-imx-3stack-bt.o
+snd-soc-imx-3stack-cs42888-objs := imx-3stack-cs42888.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_CS42888) += snd-soc-imx-3stack-cs42888.o
+
diff --git a/sound/soc/imx/imx-3stack-ak4647.c b/sound/soc/imx/imx-3stack-ak4647.c
new file mode 100644
index 000000000000..56c7a498e5b1
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-ak4647.c
@@ -0,0 +1,445 @@
+/*
+ * imx-3stack-ak4647.c -- SoC audio for imx_3stack
+ *
+ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <mach/clock.h>
+#include <linux/regulator/consumer.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+
+#define AK4647_SSI_MASTER 1
+
+extern struct snd_soc_dai ak4647_hifi_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4647;
+
+static void headphone_detect_handler(struct work_struct *work);
+static DECLARE_WORK(hp_event, headphone_detect_handler);
+static int ak4647_jack_func;
+static int ak4647_spk_func;
+
+struct imx_3stack_priv {
+ struct platform_device *pdev;
+};
+
+static struct imx_3stack_priv card_priv;
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ /* AK4647 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if AK4647_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static int imx_3stack_hifi_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ int ret = 0;
+ u32 dai_format;
+
+#if AK4647_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#endif
+
+ ssi_mode->sync_mode = 1;
+ if (channels == 1)
+ ssi_mode->network_mode = 0;
+ else
+ ssi_mode->network_mode = 1;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc, 2);
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, rate, 0);
+
+ /* set codec BCLK division for sample rate */
+ snd_soc_dai_set_clkdiv(codec_dai, 0, 0);
+
+ return 0;
+}
+
+/*
+ * imx_3stack ak4647 HiFi DAI operations.
+ */
+static struct snd_soc_ops imx_3stack_hifi_ops = {
+ .hw_params = imx_3stack_hifi_hw_params,
+};
+
+static int ak4647_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = ak4647_jack_func;
+ return 0;
+}
+
+static int ak4647_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ if (ak4647_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ ak4647_jack_func = ucontrol->value.integer.value[0];
+
+ if (ak4647_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int ak4647_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = ak4647_spk_func;
+ return 0;
+}
+
+static int ak4647_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ if (ak4647_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ ak4647_spk_func = ucontrol->value.integer.value[0];
+ if (ak4647_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Line Out Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Line Out Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+ return 0;
+}
+
+/* imx_3stack card dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* mic is connected to mic1 - with bias */
+ {"Left Input", NULL, "Mic1 Jack"},
+
+ /* Line in jack */
+ {"Left Input", NULL, "Line In Jack"},
+ {"Right Input", NULL, "Line In Jack"},
+
+ /* Headphone jack */
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ /* Line out jack */
+ {"Line Out Jack", NULL, "LOUT"},
+
+ /* Ext Spk */
+ {"Ext Spk", NULL, "LOUT"},
+
+};
+
+static const char *jack_function[] = { "off", "on" };
+
+static const char *spk_function[] = { "off", "on" };
+
+static const struct soc_enum ak4647_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new ak4647_card_controls[] = {
+ SOC_ENUM_EXT("Jack Function", ak4647_enum[0], ak4647_get_jack,
+ ak4647_set_jack),
+ SOC_ENUM_EXT("Speaker Function", ak4647_enum[1], ak4647_get_spk,
+ ak4647_set_spk),
+};
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+}
+
+static irqreturn_t imx_headphone_detect_handler(int irq, void *dev_id)
+{
+ schedule_work(&hp_event);
+ return IRQ_HANDLED;
+
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ unsigned int value;
+
+ value = plat->hp_status();
+
+ if (value == 0)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static int imx_3stack_ak4647_init(struct snd_soc_codec *codec)
+{
+ int i, ret;
+ for (i = 0; i < ARRAY_SIZE(ak4647_card_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak4647_card_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "ak4647",
+ .stream_name = "ak4647",
+ .codec_dai = &ak4647_hifi_dai,
+ .init = imx_3stack_ak4647_init,
+ .ops = &imx_3stack_hifi_ops,
+};
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_ak4647,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ * It also registers devices for platform DMA, I2S, SSP and registers an
+ * I2C driver to probe the codec.
+ */
+static int __init imx_3stack_ak4647_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &card_priv;
+ struct snd_soc_dai *ak4647_cpu_dai;
+ int ret = 0;
+
+ dev_data->init();
+
+ if (dev_data->src_port == 1)
+ ak4647_cpu_dai = imx_ssi_dai[0];
+ else
+ ak4647_cpu_dai = imx_ssi_dai[2];
+
+ imx_3stack_dai.cpu_dai = ak4647_cpu_dai;
+
+ imx_3stack_init_dam(dev_data->src_port, dev_data->ext_port);
+
+ ret = request_irq(dev_data->intr_id_hp, imx_headphone_detect_handler, 0,
+ "headphone", NULL);
+ if (ret < 0)
+ goto err;
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0)
+ goto sysfs_err;
+
+ priv->pdev = pdev;
+ return ret;
+
+sysfs_err:
+ free_irq(dev_data->intr_id_hp, NULL);
+err:
+ return ret;
+}
+
+static int __devexit imx_3stack_ak4647_remove(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ free_irq(dev_data->intr_id_hp, NULL);
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+ return 0;
+}
+
+static struct platform_driver imx_3stack_ak4647_driver = {
+ .probe = imx_3stack_ak4647_probe,
+ .remove = __devexit_p(imx_3stack_ak4647_remove),
+ .driver = {
+ .name = "imx-3stack-ak4647",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_ak4647_driver);
+ if (ret < 0)
+ goto exit;
+
+ if (snd_soc_card_imx_3stack.codec == NULL) {
+ ret = -ENOMEM;
+ goto err_device_alloc;
+ }
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 3);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+err_device_alloc:
+ platform_driver_unregister(&imx_3stack_ak4647_driver);
+exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_ak4647_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC ak4647 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-ak5702.c b/sound/soc/imx/imx-3stack-ak5702.c
new file mode 100644
index 000000000000..d90252b729fd
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-ak5702.c
@@ -0,0 +1,226 @@
+/*
+ * imx-3stack-ak5702.c -- SoC audio for imx_3stack
+ *
+ * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/ak5702.h"
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+static struct imx_3stack_pcm_state clk_state;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+
+ /* disable the PLL if there are no active Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ struct imx_esai *esai_mode = (struct imx_esai *)cpu_dai->private_data;
+ u32 dai_format;
+
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ esai_mode->sync_mode = 0;
+ esai_mode->network_mode = 1;
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffff, 0xffffffff, 2, 0);
+
+ /* set the ESAI system clock as input */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ /* set codec BCLK division */
+ snd_soc_dai_set_clkdiv(codec_dai, AK5702_BCLK_CLKDIV,
+ AK5702_BCLK_DIV_32);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_pll(codec_dai, 1, 0, 12000000, 0);
+ return 0;
+}
+
+/*
+ * imx_3stack ak5702 DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+/* imx_3stack card dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+/* example card audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line in jack */
+ {"ADCA Left Input", NULL, "Line In Jack"},
+ {"ADCA Right Input", NULL, "Line In Jack"},
+ {"ADCB Left Input", NULL, "Line In Jack"},
+ {"ADCB Right Input", NULL, "Line In Jack"},
+};
+
+static int imx_3stack_ak5702_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "ak5702",
+ .stream_name = "ak5702",
+ .codec_dai = &ak5702_dai,
+ .init = imx_3stack_ak5702_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_card_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_card_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_ak5702,
+};
+
+static int __devinit imx_3stack_ak5702_probe(struct platform_device *pdev)
+{
+ struct ak5702_setup_data *setup;
+
+ imx_3stack_dai.cpu_dai = &imx_esai_dai[2];
+
+ setup = kzalloc(sizeof(struct ak5702_setup_data), GFP_KERNEL);
+ setup->i2c_bus = 1;
+ setup->i2c_address = 0x13;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ return 0;
+}
+
+static int imx_3stack_ak5702_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver imx_3stack_ak5702_driver = {
+ .probe = imx_3stack_ak5702_probe,
+ .remove = imx_3stack_ak5702_remove,
+ .driver = {
+ .name = "imx-3stack-ak5702",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_ak5702_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_ak5702_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ALSA SoC ak5702 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-bt.c b/sound/soc/imx/imx-3stack-bt.c
new file mode 100644
index 000000000000..4b6e36b629e6
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-bt.c
@@ -0,0 +1,256 @@
+/*
+ * imx-3stack-bt.c -- SoC bluetooth audio for imx_3stack
+ *
+ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+#include "imx-3stack-bt.h"
+
+#define BT_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ struct platform_device *pdev;
+ int active;
+};
+
+static struct imx_3stack_priv card_priv;
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ /* bt uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if BT_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static int imx_3stack_bt_startup(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+
+ if (!priv->active)
+ gpio_activate_bt_audio_port();
+ priv->active++;
+ return 0;
+}
+
+static int imx_3stack_bt_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ unsigned int channels = params_channels(params);
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ int ret = 0;
+ u32 dai_format;
+
+#if BT_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBM_CFM;
+#else
+ dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#endif
+
+ ssi_mode->sync_mode = 1;
+ if (channels == 1)
+ ssi_mode->network_mode = 0;
+ else
+ ssi_mode->network_mode = 1;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc, 2);
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static void imx_3stack_bt_shutdown(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+
+ priv->active--;
+ if (!priv->active)
+ gpio_inactivate_bt_audio_port();
+}
+
+/*
+ * imx_3stack bt DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_bt_ops = {
+ .startup = imx_3stack_bt_startup,
+ .hw_params = imx_3stack_bt_hw_params,
+ .shutdown = imx_3stack_bt_shutdown,
+};
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "bluetooth",
+ .stream_name = "bluetooth",
+ .codec_dai = &bt_dai,
+ .ops = &imx_3stack_bt_ops,
+};
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_bt,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ * It also registers devices for platform DMA, I2S, SSP and registers an
+ * I2C driver to probe the codec.
+ */
+static int __init imx_3stack_bt_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &card_priv;
+ struct snd_soc_dai *bt_cpu_dai;
+
+ if (dev_data->src_port == 1)
+ bt_cpu_dai = imx_ssi_dai[0];
+ else
+ bt_cpu_dai = imx_ssi_dai[2];
+
+ bt_cpu_dai->dev = &pdev->dev;
+ imx_3stack_dai.cpu_dai = bt_cpu_dai;
+
+ /* Configure audio port */
+ imx_3stack_init_dam(dev_data->src_port, dev_data->ext_port);
+
+ priv->pdev = pdev;
+ priv->active = 0;
+ return 0;
+
+}
+
+static int __devexit imx_3stack_bt_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver imx_3stack_bt_driver = {
+ .probe = imx_3stack_bt_probe,
+ .remove = __devexit_p(imx_3stack_bt_remove),
+ .driver = {
+ .name = "imx-3stack-bt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_bt_driver);
+ if (ret < 0)
+ goto exit;
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 4);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+err_device_alloc:
+ platform_driver_unregister(&imx_3stack_bt_driver);
+exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_bt_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC bluetooth imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-bt.h b/sound/soc/imx/imx-3stack-bt.h
new file mode 100644
index 000000000000..50ff66bd738e
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-bt.h
@@ -0,0 +1,21 @@
+/*
+ * imx-3stack-bt.h -- Bluetooth PCM driver header file for Freescale IMX
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _MXC_BTPCM_H
+#define _MXC_BTPCM_H
+
+extern struct snd_soc_dai bt_dai;
+extern struct snd_soc_codec_device soc_codec_dev_bt;
+#endif
diff --git a/sound/soc/imx/imx-3stack-cs42888.c b/sound/soc/imx/imx-3stack-cs42888.c
new file mode 100644
index 000000000000..4d91be6effca
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-cs42888.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/soc-dai.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/cs42888.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/mxc_asrc.h>
+#endif
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static unsigned int asrc_rates[] = {
+ 0,
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+
+static struct asrc_esai asrc_esai_data;
+
+#endif
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+static struct imx_3stack_pcm_state clk_state;
+unsigned int mclk_freq;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ asrc_esai_data.cpu_dai_rates = cpu_dai->playback.rates;
+ asrc_esai_data.codec_dai_rates = codec_dai->playback.rates;
+ cpu_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ codec_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ }
+#endif
+
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ codec_dai->playback.rates = asrc_esai_data.codec_dai_rates;
+ cpu_dai->playback.rates = asrc_esai_data.cpu_dai_rates;
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ }
+#endif
+
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ u32 dai_format;
+ unsigned int lrclk_ratio = 0;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ unsigned int channel = params_channels(params);
+#endif
+ struct imx_esai *esai_mode = (struct imx_esai *)cpu_dai->private_data;
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ unsigned int asrc_input_rate = rate;
+ struct mxc_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config;
+ int retVal = 0;;
+
+ retVal = asrc_req_pair(channel, &asrc_esai_data.asrc_index);
+ if (retVal < 0) {
+ pr_err("Fail to request asrc pair\n");
+ return -1;
+ }
+
+ config.pair = asrc_esai_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = asrc_input_rate;
+ config.output_sample_rate = asrc_esai_data.output_sample_rate;
+ config.inclk = INCLK_NONE;
+ config.word_width = 32;
+ config.outclk = OUTCLK_ESAI_TX;
+ retVal = asrc_config_pair(&config);
+ if (retVal < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ return retVal;
+ }
+ rate = asrc_esai_data.output_sample_rate;
+ pcm_data->asrc_index = asrc_esai_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+ }
+#endif
+
+ switch (rate) {
+ case 32000:
+ lrclk_ratio = 3;
+ break;
+ case 48000:
+ lrclk_ratio = 3;
+ break;
+ case 64000:
+ lrclk_ratio = 1;
+ break;
+ case 96000:
+ lrclk_ratio = 1;
+ break;
+ case 128000:
+ lrclk_ratio = 1;
+ break;
+ case 44100:
+ lrclk_ratio = 3;
+ break;
+ case 88200:
+ lrclk_ratio = 1;
+ break;
+ case 176400:
+ lrclk_ratio = 0;
+ break;
+ case 192000:
+ lrclk_ratio = 0;
+ break;
+ default:
+ pr_info("Rate not support.\n");
+ return -EINVAL;;
+ }
+
+ dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ esai_mode->sync_mode = 0;
+ esai_mode->network_mode = 1;
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 32);
+ /* set the ESAI system clock as output */
+ snd_soc_dai_set_sysclk(cpu_dai, ESAI_CLK_EXTAL,
+ mclk_freq, SND_SOC_CLOCK_OUT);
+ /* set the ratio */
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_TX_DIV_PSR, 1);
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_TX_DIV_PM, 0);
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_TX_DIV_FP, lrclk_ratio);
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_RX_DIV_PSR, 1);
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_RX_DIV_PM, 0);
+ snd_soc_dai_set_clkdiv(cpu_dai, ESAI_RX_DIV_FP, lrclk_ratio);
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+ /* set codec Master clock */
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line out jack */
+ {"Line Out Jack", NULL, "AOUT1L"},
+ {"Line Out Jack", NULL, "AOUT1R"},
+ {"Line Out Jack", NULL, "AOUT2L"},
+ {"Line Out Jack", NULL, "AOUT2R"},
+ {"Line Out Jack", NULL, "AOUT3L"},
+ {"Line Out Jack", NULL, "AOUT3R"},
+ {"Line Out Jack", NULL, "AOUT4L"},
+ {"Line Out Jack", NULL, "AOUT4R"},
+ {"AIN1L", NULL, "Line In Jack"},
+ {"AIN1R", NULL, "Line In Jack"},
+ {"AIN2L", NULL, "Line In Jack"},
+ {"AIN2R", NULL, "Line In Jack"},
+};
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static int asrc_func;
+
+static const char *asrc_function[] = {
+ "disable", "32KHz", "44.1KHz",
+ "48KHz", "64KHz", "88.2KHz", "96KHz", "176.4KHz", "192KHz"
+};
+
+static const struct soc_enum asrc_enum[] = {
+ SOC_ENUM_SINGLE_EXT(9, asrc_function),
+};
+
+static int asrc_get_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = asrc_func;
+ return 0;
+}
+
+static int asrc_set_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (asrc_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ asrc_func = ucontrol->value.enumerated.item[0];
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func + 4];
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new asrc_controls[] = {
+ SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate,
+ asrc_set_rate),
+};
+
+#endif
+
+static int imx_3stack_cs42888_init(struct snd_soc_codec *codec)
+{
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ int i;
+ int ret;
+ for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&asrc_controls[i], codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func + 4];
+#endif
+
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "cs42888",
+ .stream_name = "cs42888",
+ .codec_dai = &cs42888_dai,
+ .init = imx_3stack_cs42888_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_card_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_card_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_device_cs42888,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ */
+static int __devinit imx_3stack_cs42888_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat_data = pdev->dev.platform_data;
+ imx_3stack_dai.cpu_dai = &imx_esai_dai[2];
+ imx_3stack_dai.cpu_dai->dev = &pdev->dev;
+ mclk_freq = plat_data->sysclk;
+ if (!(mclk_freq % 44100)) {
+ imx_3stack_dai.codec_dai->playback.rates =
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400;
+ imx_3stack_dai.codec_dai->capture.rates =
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400;
+ }
+ if (!(mclk_freq % 48000)) {
+ imx_3stack_dai.codec_dai->playback.rates =
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000;
+ imx_3stack_dai.codec_dai->capture.rates =
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000;
+ }
+
+ return 0;
+}
+
+static int __devexit imx_3stack_cs42888_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver imx_3stack_cs42888_driver = {
+ .probe = imx_3stack_cs42888_probe,
+ .remove = __devexit_p(imx_3stack_cs42888_remove),
+ .driver = {
+ .name = "imx-3stack-cs42888",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&imx_3stack_cs42888_driver);
+ if (ret < 0)
+ goto exit;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 1);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret && snd_soc_card_imx_3stack.codec != NULL)
+ goto exit;
+
+ platform_device_unregister(imx_3stack_snd_device);
+err_device_alloc:
+ platform_driver_unregister(&imx_3stack_cs42888_driver);
+exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_cs42888_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ALSA SoC cs42888 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-sgtl5000.c b/sound/soc/imx/imx-3stack-sgtl5000.c
new file mode 100644
index 000000000000..cccc0bda6150
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-sgtl5000.c
@@ -0,0 +1,706 @@
+/*
+ * imx-3stack-sgtl5000.c -- i.MX 3Stack Driver for Freescale SGTL5000 Codec
+ *
+ * Copyright (C) 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Revision history
+ * 21th Oct 2008 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/fsl_devices.h>
+#include <linux/slab.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/initval.h>
+
+#include <mach/dma.h>
+#include <mach/clock.h>
+
+#include "../codecs/sgtl5000.h"
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/mxc_asrc.h>
+
+static unsigned int sgtl5000_rates[] = {
+ 0,
+ 32000,
+ 44100,
+ 48000,
+ 96000,
+};
+
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+
+static struct asrc_esai asrc_ssi_data;
+#endif
+
+/* SSI BCLK and LRC master */
+#define SGTL5000_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ int sysclk;
+ int hw;
+ struct platform_device *pdev;
+};
+
+static struct imx_3stack_priv card_priv;
+
+static int imx_3stack_audio_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_link *machine = rtd->dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &card_priv;
+ unsigned int rate = params_rate(params);
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ int ret = 0;
+
+ unsigned int channels = params_channels(params);
+ u32 dai_format;
+
+ /* only need to do this once as capture and playback are sync */
+ if (priv->hw)
+ return 0;
+ priv->hw = 1;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if ((asrc_ssi_data.output_sample_rate != 0)
+ && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
+ unsigned int asrc_input_rate = rate;
+ unsigned int channel = params_channels(params);
+ struct mxc_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config;
+ struct mxc_audio_platform_data *plat;
+ struct imx_3stack_priv *priv = &card_priv;
+ int retVal = 0;
+ retVal = asrc_req_pair(channel, &asrc_ssi_data.asrc_index);
+ if (retVal < 0) {
+ pr_err("asrc_req_pair fail\n");
+ return -1;
+ }
+ config.pair = asrc_ssi_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = asrc_input_rate;
+ config.output_sample_rate = asrc_ssi_data.output_sample_rate;
+ config.inclk = INCLK_NONE;
+ config.word_width = 32;
+ plat = priv->pdev->dev.platform_data;
+ if (plat->src_port == 1)
+ config.outclk = OUTCLK_SSI1_TX;
+ else
+ config.outclk = OUTCLK_SSI2_TX;
+ retVal = asrc_config_pair(&config);
+ if (retVal < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_ssi_data.asrc_index);
+ return retVal;
+ }
+ rate = asrc_ssi_data.output_sample_rate;
+ pcm_data->asrc_index = asrc_ssi_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+ }
+#endif
+
+ snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, priv->sysclk, 0);
+ snd_soc_dai_set_sysclk(codec_dai, SGTL5000_LRCLK, rate, 0);
+
+#if SGTL5000_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#endif
+
+ ssi_mode->sync_mode = 1;
+ if (channels == 1)
+ ssi_mode->network_mode = 0;
+ else
+ ssi_mode->network_mode = 1;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ 2, 32);
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (asrc_ssi_data.output_sample_rate != 0) {
+ struct snd_soc_pcm_runtime *rtd =
+ substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ asrc_ssi_data.cpu_dai_rates = cpu_dai->playback.rates;
+ asrc_ssi_data.codec_dai_rates =
+ codec_dai->playback.rates;
+ cpu_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ codec_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ }
+ }
+#endif
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (asrc_ssi_data.output_sample_rate != 0) {
+ struct snd_soc_pcm_runtime *rtd =
+ substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ codec_dai->playback.rates =
+ asrc_ssi_data.codec_dai_rates;
+ cpu_dai->playback.rates = asrc_ssi_data.cpu_dai_rates;
+ asrc_release_pair(asrc_ssi_data.asrc_index);
+ }
+ }
+#endif
+
+ priv->hw = 0;
+}
+
+/*
+ * imx_3stack SGTL5000 audio DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_audio_hw_params,
+};
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+ /* SGTL5000 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if SGTL5000_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+/* imx_3stack machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Mic Jack --> MIC_IN (with automatic bias) */
+ {"MIC_IN", NULL, "Mic Jack"},
+
+ /* Line in Jack --> LINE_IN */
+ {"LINE_IN", NULL, "Line In Jack"},
+
+ /* HP_OUT --> Headphone Jack */
+ {"Headphone Jack", NULL, "HP_OUT"},
+
+ /* LINE_OUT --> Ext Speaker */
+ {"Ext Spk", NULL, "LINE_OUT"},
+};
+
+static int sgtl5000_jack_func;
+static int sgtl5000_spk_func;
+static int sgtl5000_line_in_func;
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ int hp_status;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+ hp_status = plat->hp_status();
+ if (hp_status)
+ set_irq_type(plat->hp_irq, IRQ_TYPE_EDGE_FALLING);
+ else
+ set_irq_type(plat->hp_irq, IRQ_TYPE_EDGE_RISING);
+ enable_irq(plat->hp_irq);
+}
+
+static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler);
+
+static irqreturn_t imx_headphone_detect_handler(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+ schedule_delayed_work(&hp_event, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ u16 hp_status;
+
+ /* determine whether hp is plugged in */
+ hp_status = plat->hp_status();
+
+ if (hp_status == 0)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static const char *jack_function[] = { "off", "on"};
+
+static const char *spk_function[] = { "off", "on" };
+
+static const char *line_in_function[] = { "off", "on" };
+
+static const struct soc_enum sgtl5000_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, line_in_function),
+};
+
+static int sgtl5000_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = sgtl5000_jack_func;
+ return 0;
+}
+
+static int sgtl5000_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (sgtl5000_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ sgtl5000_jack_func = ucontrol->value.enumerated.item[0];
+ if (sgtl5000_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int sgtl5000_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = sgtl5000_spk_func;
+ return 0;
+}
+
+static int sgtl5000_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (sgtl5000_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ sgtl5000_spk_func = ucontrol->value.enumerated.item[0];
+ if (sgtl5000_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int sgtl5000_get_line_in(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = sgtl5000_line_in_func;
+ return 0;
+}
+
+static int sgtl5000_set_line_in(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (sgtl5000_line_in_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ sgtl5000_line_in_func = ucontrol->value.enumerated.item[0];
+ if (sgtl5000_line_in_func)
+ snd_soc_dapm_enable_pin(codec, "Line In Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Line In Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+
+ return 0;
+}
+
+/* imx_3stack card dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new sgtl5000_machine_controls[] = {
+ SOC_ENUM_EXT("Jack Function", sgtl5000_enum[0], sgtl5000_get_jack,
+ sgtl5000_set_jack),
+ SOC_ENUM_EXT("Speaker Function", sgtl5000_enum[1], sgtl5000_get_spk,
+ sgtl5000_set_spk),
+ SOC_ENUM_EXT("Line In Function", sgtl5000_enum[1], sgtl5000_get_line_in,
+ sgtl5000_set_line_in),
+};
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static int asrc_func;
+
+static const char *asrc_function[] = {
+ "disable", "32KHz", "44.1KHz", "48KHz", "96KHz" };
+
+static const struct soc_enum asrc_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, asrc_function),
+};
+
+static int asrc_get_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = asrc_func;
+ return 0;
+}
+
+static int asrc_set_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (asrc_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ asrc_func = ucontrol->value.enumerated.item[0];
+ asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func];
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new asrc_controls[] = {
+ SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate,
+ asrc_set_rate),
+};
+#endif
+
+static int imx_3stack_sgtl5000_init(struct snd_soc_codec *codec)
+{
+ int i, ret;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&asrc_controls[i], codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+ asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func];
+#endif
+
+ /* Add imx_3stack specific controls */
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_machine_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&sgtl5000_machine_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Add imx_3stack specific widgets */
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ /* Set up imx_3stack specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_disable_pin(codec, "Line In Jack");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/* imx_3stack digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "SGTL5000",
+ .stream_name = "SGTL5000",
+ .codec_dai = &sgtl5000_dai,
+ .init = imx_3stack_sgtl5000_init,
+ .ops = &imx_3stack_ops,
+};
+
+static int imx_3stack_card_remove(struct platform_device *pdev)
+{
+ struct imx_3stack_priv *priv = &card_priv;
+ struct mxc_audio_platform_data *plat;
+ if (priv->pdev) {
+ plat = priv->pdev->dev.platform_data;
+ if (plat->finit)
+ plat->finit();
+ }
+
+ return 0;
+}
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_card_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_sgtl5000,
+};
+
+static int __devinit imx_3stack_sgtl5000_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &card_priv;
+ struct snd_soc_dai *sgtl5000_cpu_dai;
+ struct sgtl5000_setup_data *setup;
+
+ int ret = 0;
+
+ priv->pdev = pdev;
+
+ gpio_activate_audio_ports();
+ imx_3stack_init_dam(plat->src_port, plat->ext_port);
+
+ if (plat->src_port == 2)
+ sgtl5000_cpu_dai = imx_ssi_dai[2];
+ else if (plat->src_port == 1)
+ sgtl5000_cpu_dai = imx_ssi_dai[0];
+ else if (plat->src_port == 7)
+ sgtl5000_cpu_dai = imx_ssi_dai[4];
+
+
+ imx_3stack_dai.cpu_dai = sgtl5000_cpu_dai;
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0) {
+ pr_err("%s:failed to create driver_attr_headphone\n", __func__);
+ goto sysfs_err;
+ }
+
+ ret = -EINVAL;
+ if (plat->init && plat->init())
+ goto err_plat_init;
+
+ priv->sysclk = plat->sysclk;
+
+ /* The SGTL5000 has an internal reset that is deasserted 8 SYS_MCLK
+ cycles after all power rails have been brought up. After this time
+ communication can start */
+
+ if (plat->hp_status())
+ ret = request_irq(plat->hp_irq,
+ imx_headphone_detect_handler,
+ IRQ_TYPE_EDGE_FALLING, pdev->name, priv);
+ else
+ ret = request_irq(plat->hp_irq,
+ imx_headphone_detect_handler,
+ IRQ_TYPE_EDGE_RISING, pdev->name, priv);
+ if (ret < 0) {
+ pr_err("%s: request irq failed\n", __func__);
+ goto err_card_reg;
+ }
+
+ setup = kzalloc(sizeof(struct sgtl5000_setup_data), GFP_KERNEL);
+ if (!setup) {
+ pr_err("%s: kzalloc sgtl5000_setup_data failed\n", __func__);
+ goto err_card_reg;
+ }
+ setup->clock_enable = plat->clock_enable;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ sgtl5000_jack_func = 1;
+ sgtl5000_spk_func = 1;
+ sgtl5000_line_in_func = 0;
+
+ return 0;
+
+err_card_reg:
+ if (plat->finit)
+ plat->finit();
+err_plat_init:
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+sysfs_err:
+ return ret;
+}
+
+static int imx_3stack_sgtl5000_remove(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &card_priv;
+
+ free_irq(plat->hp_irq, priv);
+
+ if (plat->finit)
+ plat->finit();
+
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+
+ return 0;
+}
+
+static struct platform_driver imx_3stack_sgtl5000_audio_driver = {
+ .probe = imx_3stack_sgtl5000_probe,
+ .remove = imx_3stack_sgtl5000_remove,
+ .driver = {
+ .name = "imx-3stack-sgtl5000",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_sgtl5000_audio_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 2);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_sgtl5000_audio_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_init);
+module_exit(imx_3stack_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SGTL5000 Driver for i.MX 3STACK");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-wm8350.c b/sound/soc/imx/imx-3stack-wm8350.c
new file mode 100644
index 000000000000..1cd51d31e128
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-wm8350.c
@@ -0,0 +1,696 @@
+/*
+ * imx-3stack-wm8350.c -- i.MX 3Stack Driver for Wolfson WM8350 Codec
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc.
+ *
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Revision history
+ * 19th Jun 2007 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.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/initval.h>
+
+#include <mach/dma.h>
+#include <mach/clock.h>
+
+#include "../codecs/wm8350.h"
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+void gpio_activate_audio_ports(void);
+
+/* SSI BCLK and LRC master */
+#define WM8350_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ int lr_clk_active;
+ int playback_active;
+ int capture_active;
+ struct platform_device *pdev;
+ struct wm8350 *wm8350;
+};
+
+static struct imx_3stack_priv machine_priv;
+
+struct _wm8350_audio {
+ unsigned int channels;
+ snd_pcm_format_t format;
+ unsigned int rate;
+ unsigned int sysclk;
+ unsigned int bclkdiv;
+ unsigned int clkdiv;
+ unsigned int lr_rate;
+};
+
+/* in order of power consumption per rate (lowest first) */
+static const struct _wm8350_audio wm8350_audio[] = {
+ /* 16bit mono modes */
+
+ {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
+ WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
+ WM8350_BCLK_DIV_24, WM8350_DACDIV_6, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
+ WM8350_BCLK_DIV_12, WM8350_DACDIV_3, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
+ WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
+ WM8350_BCLK_DIV_16, WM8350_DACDIV_4, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+
+ /* 16 bit stereo modes */
+ {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
+ WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
+ WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
+ WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
+ WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
+ WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+
+ /* 24bit stereo modes */
+ {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+};
+
+#if WM8350_SSI_MASTER
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+
+ /* In master mode the LR clock can come from either the DAC or ADC.
+ * We use the LR clock from whatever stream is enabled first.
+ */
+
+ if (!priv->lr_clk_active) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ else
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ }
+ priv->lr_clk_active++;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->capture_active = 1;
+ else
+ priv->playback_active = 1;
+ return 0;
+}
+#else
+#define imx_3stack_startup NULL
+#endif
+
+static int imx_3stack_audio_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_link *machine = rtd->dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &machine_priv;
+ int ret = 0;
+ int i, found = 0;
+ snd_pcm_format_t format = params_format(params);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ u32 dai_format;
+
+ /* only need to do this once as capture and playback are sync */
+ if (priv->lr_clk_active > 1)
+ return 0;
+
+ /* find the correct audio parameters */
+ for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
+ if (rate == wm8350_audio[i].rate &&
+ format == wm8350_audio[i].format &&
+ channels == wm8350_audio[i].channels) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printk(KERN_ERR "%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+#if WM8350_SSI_MASTER
+ /* codec FLL input is 32768 kHz from MCLK */
+ snd_soc_dai_set_pll(codec_dai, 0, 32768, wm8350_audio[i].sysclk);
+#else
+ /* codec FLL input is rate from DAC LRC */
+ snd_soc_dai_set_pll(codec_dai, 0, rate, wm8350_audio[i].sysclk);
+#endif
+
+#if WM8350_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ ssi_mode->sync_mode = 1;
+ if (channels == 1)
+ ssi_mode->network_mode = 0;
+ else
+ ssi_mode->network_mode = 1;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dai_format &= ~SND_SOC_DAIFMT_INV_MASK;
+ /* Invert frame to switch mic from right channel to left */
+ dai_format |= SND_SOC_DAIFMT_NB_IF;
+ }
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ channels);
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set 32KHZ as the codec system clock for DAC and ADC */
+ snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_32K,
+ wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ channels);
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set DAC LRC as the codec system clock for DAC and ADC */
+ snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_DAC,
+ wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
+#endif
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ /* set codec BCLK division for sample rate */
+ snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
+ wm8350_audio[i].bclkdiv);
+
+ /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_DACLR_CLKDIV,
+ wm8350_audio[i].lr_rate);
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_ADCLR_CLKDIV,
+ wm8350_audio[i].lr_rate);
+
+ /* now configure DAC and ADC clocks */
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
+
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
+
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->card->codec;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = codec->control_data;
+
+ /* disable the PLL if there are no active Tx or Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+ priv->lr_clk_active--;
+
+ /*
+ * We need to keep track of active streams in master mode and
+ * switch LRC source if necessary.
+ */
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->capture_active = 0;
+ else
+ priv->playback_active = 0;
+
+ if (priv->capture_active)
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ else if (priv->playback_active)
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+}
+
+/*
+ * imx_3stack WM8350 HiFi DAI operations.
+ */
+static struct snd_soc_ops imx_3stack_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_audio_hw_params,
+};
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+ /* WM8350 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if WM8350_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* SiMIC --> IN1LN (with automatic bias) via SP1 */
+ {"IN1RP", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "SiMIC"},
+
+ /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
+ {"IN1LN", NULL, "Mic Bias"},
+ {"IN1LP", NULL, "Mic1 Jack"},
+ {"Mic Bias", NULL, "Mic1 Jack"},
+
+ /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
+ {"IN1RN", NULL, "Mic2 Jack"},
+ {"IN1RP", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic2 Jack"},
+
+ /* Line in Jack --> AUX (L+R) */
+ {"IN3R", NULL, "Line In Jack"},
+ {"IN3L", NULL, "Line In Jack"},
+
+ /* Out1 --> Headphone Jack */
+ {"Headphone Jack", NULL, "OUT1R"},
+ {"Headphone Jack", NULL, "OUT1L"},
+
+ /* Out1 --> Line Out Jack */
+ {"Line Out Jack", NULL, "OUT2R"},
+ {"Line Out Jack", NULL, "OUT2L"},
+};
+
+static int wm8350_jack_func;
+static int wm8350_spk_func;
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+ wm8350_unmask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+}
+
+static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler);
+
+static void imx_3stack_jack_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ schedule_delayed_work(&hp_event, msecs_to_jiffies(200));
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ u16 reg;
+
+ reg = wm8350_reg_read(priv->wm8350, WM8350_JACK_PIN_STATUS);
+
+ if (reg & WM8350_JACK_R_LVL)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static const char *jack_function[] = { "off", "on"
+};
+
+static const char *spk_function[] = { "off", "on" };
+
+static const struct soc_enum wm8350_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static int wm8350_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = wm8350_jack_func;
+ return 0;
+}
+
+static int wm8350_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (wm8350_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wm8350_jack_func = ucontrol->value.enumerated.item[0];
+ if (wm8350_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int wm8350_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = wm8350_spk_func;
+ return 0;
+}
+
+static int wm8350_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (wm8350_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wm8350_spk_func = ucontrol->value.enumerated.item[0];
+ if (wm8350_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Line Out Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Line Out Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("SiMIC", NULL),
+ SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new wm8350_machine_controls[] = {
+ SOC_ENUM_EXT("Jack Function", wm8350_enum[0], wm8350_get_jack,
+ wm8350_set_jack),
+ SOC_ENUM_EXT("Speaker Function", wm8350_enum[1], wm8350_get_spk,
+ wm8350_set_spk),
+};
+
+static int imx_3stack_wm8350_init(struct snd_soc_codec *codec)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+ int i, ret;
+
+ codec->control_data = wm8350;
+
+ /* Add imx_3stack specific controls */
+ for (i = 0; i < ARRAY_SIZE(wm8350_machine_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8350_machine_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Add imx_3stack specific widgets */
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ /* Set up imx_3stack specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+
+}
+
+/* imx_3stack digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "WM8350",
+ .stream_name = "WM8350",
+ .codec_dai = &wm8350_dai,
+ .init = imx_3stack_wm8350_init,
+ .ops = &imx_3stack_ops,
+};
+
+static int imx_3stack_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ socdev->codec_data = wm8350;
+
+ return 0;
+}
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .probe = imx_3stack_machine_probe,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_wm8350,
+};
+
+static int __devinit imx_3stack_wm8350_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = plat->priv;
+ struct snd_soc_dai *wm8350_cpu_dai;
+ int ret = 0;
+ u16 reg;
+
+ priv->pdev = pdev;
+ priv->wm8350 = wm8350;
+
+ gpio_activate_audio_ports();
+ imx_3stack_init_dam(plat->src_port, plat->ext_port);
+
+ if (plat->src_port == 2)
+ wm8350_cpu_dai = imx_ssi_dai[2];
+ else
+ wm8350_cpu_dai = imx_ssi_dai[0];
+
+ imx_3stack_dai.cpu_dai = wm8350_cpu_dai;
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0) {
+ pr_err("%s:failed to create driver_attr_headphone\n", __func__);
+ return ret;
+ }
+
+ /* enable slow clock gen for jack detect */
+ reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_4);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_4, reg | WM8350_TOCLK_ENA);
+ /* enable jack detect */
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_DETECT);
+ wm8350_reg_write(wm8350, WM8350_JACK_DETECT, reg | WM8350_JDR_ENA);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
+ imx_3stack_jack_handler, NULL);
+ wm8350_unmask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+ wm8350_jack_func = 1;
+ wm8350_spk_func = 1;
+
+ return 0;
+}
+
+static int imx_3stack_wm8350_remove(struct platform_device *pdev)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+ return 0;
+}
+
+static struct platform_driver imx_3stack_wm8350_audio_driver = {
+ .probe = imx_3stack_wm8350_probe,
+ .remove = __devexit_p(imx_3stack_wm8350_remove),
+ .driver = {
+ .name = "wm8350-imx-3stack-audio",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_wm8350_audio_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_wm8350_audio_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_init);
+module_exit(imx_3stack_exit);
+
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("PMIC WM8350 Driver for i.MX 3STACK");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-wm8580.c b/sound/soc/imx/imx-3stack-wm8580.c
new file mode 100644
index 000000000000..d2366e9c74a1
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-wm8580.c
@@ -0,0 +1,437 @@
+/*
+ * imx-3stack-wm8580.c -- SoC 5.1 audio for imx_3stack
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/wm8580.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/mxc_asrc.h>
+#endif
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static unsigned int asrc_rates[] = {
+ 0,
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+
+static struct asrc_esai asrc_esai_data;
+
+#endif
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+static struct imx_3stack_pcm_state clk_state;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ asrc_esai_data.cpu_dai_rates = cpu_dai->playback.rates;
+ asrc_esai_data.codec_dai_rates = codec_dai->playback.rates;
+ cpu_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ codec_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ }
+#endif
+
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ codec_dai->playback.rates = asrc_esai_data.codec_dai_rates;
+ cpu_dai->playback.rates = asrc_esai_data.cpu_dai_rates;
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ }
+#endif
+
+ /* disable the PLL if there are no active Tx or Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_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_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ u32 dai_format;
+ unsigned int pll_out = 0, lrclk_ratio = 0;
+ unsigned int channel = params_channels(params);
+ struct imx_esai *esai_mode = (struct imx_esai *)cpu_dai->private_data;
+
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate >= 32000) {
+ unsigned int asrc_input_rate = rate;
+ struct mxc_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config;
+ int retVal = 0;;
+
+ retVal = asrc_req_pair(channel, &asrc_esai_data.asrc_index);
+ if (retVal < 0) {
+ pr_err("Fail to request asrc pair\n");
+ return -1;
+ }
+
+ config.pair = asrc_esai_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = asrc_input_rate;
+ config.output_sample_rate = asrc_esai_data.output_sample_rate;
+ config.inclk = INCLK_NONE;
+ config.word_width = 32;
+ config.outclk = OUTCLK_ESAI_TX;
+ retVal = asrc_config_pair(&config);
+ if (retVal < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ return retVal;
+ }
+ rate = asrc_esai_data.output_sample_rate;
+ pcm_data->asrc_index = asrc_esai_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+ }
+#endif
+
+ switch (rate) {
+ case 8000:
+ lrclk_ratio = 5;
+ pll_out = 6144000;
+ break;
+ case 11025:
+ lrclk_ratio = 4;
+ pll_out = 5644800;
+ break;
+ case 16000:
+ lrclk_ratio = 3;
+ pll_out = 6144000;
+ break;
+ case 32000:
+ lrclk_ratio = 3;
+ pll_out = 12288000;
+ break;
+ case 48000:
+ lrclk_ratio = 2;
+ pll_out = 12288000;
+ break;
+ case 64000:
+ lrclk_ratio = 1;
+ pll_out = 12288000;
+ break;
+ case 96000:
+ lrclk_ratio = 2;
+ pll_out = 24576000;
+ break;
+ case 128000:
+ lrclk_ratio = 1;
+ pll_out = 24576000;
+ break;
+ case 22050:
+ lrclk_ratio = 4;
+ pll_out = 11289600;
+ break;
+ case 44100:
+ lrclk_ratio = 2;
+ pll_out = 11289600;
+ break;
+ case 88200:
+ lrclk_ratio = 0;
+ pll_out = 11289600;
+ break;
+ case 176400:
+ lrclk_ratio = 0;
+ pll_out = 22579200;
+ break;
+ case 192000:
+ lrclk_ratio = 0;
+ pll_out = 24576000;
+ break;
+ default:
+ pr_info("Rate not support.\n");
+ return -EINVAL;;
+ }
+
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ esai_mode->sync_mode = 0;
+ esai_mode->network_mode = 1;
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channel == 1 ? 0x1 : 0x3,
+ channel == 1 ? 0x1 : 0x3,
+ 2, 0);
+
+ /* set the ESAI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA);
+ snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+ WM8580_CLKSRC_PLLA);
+
+ /* set codec LRCLK and BCLK */
+ snd_soc_dai_set_sysclk(codec_dai, WM8580_BCLK_CLKDIV, 0,
+ SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, WM8580_LRCLK_CLKDIV, lrclk_ratio,
+ SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_pll(codec_dai, 1, 0, 12000000, pll_out);
+ return 0;
+}
+
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line out jack */
+ {"Line Out Jack", NULL, "VOUT1L"},
+ {"Line Out Jack", NULL, "VOUT1R"},
+ {"Line Out Jack", NULL, "VOUT2L"},
+ {"Line Out Jack", NULL, "VOUT2R"},
+ {"Line Out Jack", NULL, "VOUT3L"},
+ {"Line Out Jack", NULL, "VOUT3R"},
+};
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static int asrc_func;
+
+static const char *asrc_function[] = {
+ "disable", "32KHz", "44.1KHz",
+ "48KHz", "64KHz", "88.2KHz", "96KHz", "176.4KHz", "192KHz"
+};
+
+static const struct soc_enum asrc_enum[] = {
+ SOC_ENUM_SINGLE_EXT(9, asrc_function),
+};
+
+static int asrc_get_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = asrc_func;
+ return 0;
+}
+
+static int asrc_set_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (asrc_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ asrc_func = ucontrol->value.enumerated.item[0];
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func + 4];
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new asrc_controls[] = {
+ SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate,
+ asrc_set_rate),
+};
+
+#endif
+
+static int imx_3stack_wm8580_init(struct snd_soc_codec *codec)
+{
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ int i;
+ int ret;
+ for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&asrc_controls[i], codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func + 4];
+#endif
+
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "wm8580",
+ .stream_name = "wm8580",
+ .codec_dai = wm8580_dai,
+ .init = imx_3stack_wm8580_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_card_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-3stack",
+ .platform = &imx_soc_platform,
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_card_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .card = &snd_soc_card_imx_3stack,
+ .codec_dev = &soc_codec_dev_wm8580,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ */
+static int __devinit imx_3stack_wm8580_probe(struct platform_device *pdev)
+{
+ struct wm8580_setup_data *setup;
+
+ imx_3stack_dai.cpu_dai = &imx_esai_dai[2];
+ imx_3stack_dai.cpu_dai->dev = &pdev->dev;
+
+ setup = kzalloc(sizeof(struct wm8580_setup_data), GFP_KERNEL);
+ setup->spi = 1;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ return 0;
+}
+
+static int __devexit imx_3stack_wm8580_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver imx_3stack_wm8580_driver = {
+ .probe = imx_3stack_wm8580_probe,
+ .remove = __devexit_p(imx_3stack_wm8580_remove),
+ .driver = {
+ .name = "imx-3stack-wm8580",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_wm8580_driver);
+ if (ret < 0)
+ goto exit;
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 1);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+ err_device_alloc:
+ platform_driver_unregister(&imx_3stack_wm8580_driver);
+ exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_wm8580_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC wm8580 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-ac97.c b/sound/soc/imx/imx-ac97.c
new file mode 100644
index 000000000000..801e98efb4f8
--- /dev/null
+++ b/sound/soc/imx/imx-ac97.c
@@ -0,0 +1,564 @@
+/*
+ * imx-ac97.c -- AC97 driver for Freescale IMX
+ *
+ *
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Revision history
+ * 26th Nov. 2009 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <linux/delay.h>
+
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+static DEFINE_MUTEX(ac97_mutex);
+static DECLARE_COMPLETION(ac97_completion);
+
+struct imx_ac97_runtime {
+ u32 id;
+ u32 playback;
+ u32 capture;
+};
+
+static struct imx_ac97_runtime imx_ac97_para;
+static struct imx_ssi imx_ac97_data[2];
+
+#define IMX_SSI_DUMP 0
+#if IMX_SSI_DUMP
+#define SSI_DUMP() \
+ do { \
+ printk(KERN_INFO "dump @ %s\n", __func__); \
+ printk(KERN_INFO "scr %x\t, %x\n", \
+ __raw_readl(SSI1_SCR), __raw_readl(SSI2_SCR)); \
+ printk(KERN_INFO "sisr %x\t, %x\n", \
+ __raw_readl(SSI1_SISR), __raw_readl(SSI2_SISR)); \
+ printk(KERN_INFO "stcr %x\t, %x\n", \
+ __raw_readl(SSI1_STCR), __raw_readl(SSI2_STCR)); \
+ printk(KERN_INFO "srcr %x\t, %x\n", \
+ __raw_readl(SSI1_SRCR), __raw_readl(SSI2_SRCR)); \
+ printk(KERN_INFO "stccr %x\t, %x\n", \
+ __raw_readl(SSI1_STCCR), __raw_readl(SSI2_STCCR)); \
+ printk(KERN_INFO "srccr %x\t, %x\n", \
+ __raw_readl(SSI1_SRCCR), __raw_readl(SSI2_SRCCR)); \
+ printk(KERN_INFO "sfcsr %x\t, %x\n", \
+ __raw_readl(SSI1_SFCSR), __raw_readl(SSI2_SFCSR)); \
+ printk(KERN_INFO "stmsk %x\t, %x\n", \
+ __raw_readl(SSI1_STMSK), __raw_readl(SSI2_STMSK)); \
+ printk(KERN_INFO "srmsk %x\t, %x\n", \
+ __raw_readl(SSI1_SRMSK), __raw_readl(SSI2_SRMSK)); \
+ printk(KERN_INFO "sier %x\t, %x\n", \
+ __raw_readl(SSI1_SIER), __raw_readl(SSI2_SIER)); \
+ printk(KERN_INFO "sacnt %x\t, %x\n", \
+ __raw_readl(SSI1_SACNT), __raw_readl(SSI2_SACNT)); \
+ printk(KERN_INFO "sacdd %x\t, %x\n", \
+ __raw_readl(SSI1_SACADD), __raw_readl(SSI2_SACADD)); \
+ printk(KERN_INFO "sacdat %x\t, %x\n", \
+ __raw_readl(SSI1_SACDAT), __raw_readl(SSI2_SACDAT)); \
+ printk(KERN_INFO "satag %x\t, %x\n", \
+ __raw_readl(SSI1_SATAG), __raw_readl(SSI2_SATAG)); \
+ printk(KERN_INFO "saccst %x\t, %x\n", \
+ __raw_readl(SSI1_SACCST), __raw_readl(SSI2_SACCST)); \
+ printk(KERN_INFO "saccen %x\t, %x\n", \
+ __raw_readl(SSI1_SACCEN), __raw_readl(SSI2_SACCEN)); \
+ printk(KERN_INFO "saccdis %x\t, %x\n", \
+ __raw_readl(SSI1_SACCDIS), __raw_readl(SSI2_SACCDIS)); \
+ } while (0);
+#else
+#define SSI_DUMP()
+#endif
+
+/*
+ * Read register value from codec, the read command is sent from
+ * AC97 slot 2 - 3. The register value is sent back at slot 2 -3
+ * in next RX frame.
+ */
+static unsigned short imx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ u32 sacdat, sier, scr;
+ mutex_lock(&ac97_mutex);
+ if (imx_ac97_para.id == IMX_DAI_AC97_1) {
+ sier = __raw_readl(SSI1_SIER);
+ scr = __raw_readl(SSI1_SCR);
+ __raw_writel((reg << 12), SSI1_SACADD);
+ __raw_writel(sier | SSI_SIER_CMDDU_EN, SSI1_SIER);
+ __raw_writel(SSI_SACNT_RD | __raw_readl(SSI1_SACNT),
+ SSI1_SACNT);
+ __raw_writel(scr | SSI_SCR_TE | SSI_SCR_RE, SSI1_SCR);
+ wait_for_completion_timeout(&ac97_completion, HZ);
+ sacdat = __raw_readl(SSI1_SACDAT);
+ __raw_writel(sier, SSI1_SIER);
+ __raw_writel(scr, SSI1_SCR);
+ } else {
+ sier = __raw_readl(SSI2_SIER);
+ scr = __raw_readl(SSI2_SCR);
+ __raw_writel((reg << 12), SSI2_SACADD);
+ __raw_writel(sier | SSI_SIER_CMDDU_EN, SSI2_SIER);
+ __raw_writel(SSI_SACNT_RD | __raw_readl(SSI2_SACNT),
+ SSI2_SACNT);
+ __raw_writel(scr | SSI_SCR_TE | SSI_SCR_RE, SSI2_SCR);
+ wait_for_completion_timeout(&ac97_completion, HZ);
+ sacdat = __raw_readl(SSI2_SACDAT);
+ __raw_writel(sier, SSI2_SIER);
+ __raw_writel(scr, SSI2_SCR);
+ }
+ mutex_unlock(&ac97_mutex);
+ return (unsigned short)(sacdat >> 4);
+
+}
+
+/*
+ * This fucntion is used to send command to codec.
+ */
+static void imx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ u32 scr;
+ mutex_lock(&ac97_mutex);
+ if (imx_ac97_para.id == IMX_DAI_AC97_1) {
+ scr = __raw_readl(SSI1_SCR);
+ __raw_writel(reg << 12, SSI1_SACADD);
+ __raw_writel(val << 4, SSI1_SACDAT);
+ __raw_writel(SSI_SACNT_WR | __raw_readl(SSI1_SACNT),
+ SSI1_SACNT);
+ __raw_writel(scr | SSI_SCR_TE | SSI_SCR_RE, SSI1_SCR);
+ udelay(100);
+ __raw_writel(scr, SSI1_SCR);
+ } else {
+ scr = __raw_readl(SSI2_SCR);
+ __raw_writel(reg << 12, SSI2_SACADD);
+ __raw_writel(val << 4, SSI2_SACDAT);
+ __raw_writel(SSI_SACNT_WR | __raw_readl(SSI2_SACNT),
+ SSI2_SACNT);
+ __raw_writel(scr | SSI_SCR_TE | SSI_SCR_RE, SSI2_SCR);
+ udelay(100);
+ __raw_writel(scr, SSI1_SCR);
+ }
+ mutex_unlock(&ac97_mutex);
+
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = imx_ac97_read,
+ .write = imx_ac97_write,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static struct clk *ssi1_clk;
+static struct clk *ssi2_clk;
+
+static int imx_ac97_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ u32 channel = params_channels(params);
+ u32 stccr, sier;
+
+ if (cpu_dai->id == IMX_DAI_AC97_1) {
+ stccr =
+ __raw_readl(SSI1_STCCR) & ~(SSI_STCCR_WL_MASK |
+ SSI_STCCR_DC_MASK);
+ sier = __raw_readl(SSI1_SIER);
+ } else {
+ stccr =
+ __raw_readl(SSI2_STCCR) & ~(SSI_STCCR_WL_MASK |
+ SSI_STCCR_DC_MASK);
+ sier = __raw_readl(SSI2_SIER);
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stccr |= SSI_STCCR_WL(16) | SSI_STCCR_DC(0x0C);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ stccr |= SSI_STCCR_WL(20) | SSI_STCCR_DC(0x0C);
+ break;
+ }
+ if (ssi_mode->ac97_rx_slots)
+ __raw_writel(ssi_mode->ac97_rx_slots, SSI1_SACCEN);
+ else
+ __raw_writel(((1 << channel) - 1) << (10 - channel),
+ SSI1_SACCEN);
+
+ sier |= SSI_SIER_TDMAE;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(stccr, SSI1_STCCR);
+ __raw_writel(sier, SSI1_SIER);
+ } else {
+ __raw_writel(stccr, SSI2_STCCR);
+ __raw_writel(sier, SSI2_SIER);
+ }
+
+ return 0;
+}
+
+static int imx_ac97_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct imx_ssi *ssi_mode = (struct imx_ssi *)cpu_dai->private_data;
+ u32 channel = params_channels(params);
+ u32 srccr, sier;
+
+ if (cpu_dai->id == IMX_DAI_AC97_1) {
+ srccr = __raw_readl(SSI1_SRCCR);
+ sier = __raw_readl(SSI1_SIER);
+ } else {
+ srccr = __raw_readl(SSI2_SRCCR);
+ sier = __raw_readl(SSI2_SIER);
+ }
+ srccr &= ~(SSI_SRCCR_WL_MASK | SSI_SRCCR_DC_MASK);
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srccr |= SSI_SRCCR_WL(16) | SSI_SRCCR_DC(0x0C);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srccr |= SSI_SRCCR_WL(20) | SSI_SRCCR_DC(0x0C);
+ break;
+ }
+
+ if (ssi_mode->ac97_tx_slots)
+ __raw_writel(ssi_mode->ac97_tx_slots, SSI1_SACCEN);
+ else
+ __raw_writel(((1 << channel) - 1) << (10 - channel),
+ SSI1_SACCEN);
+
+ /* enable interrupts */
+ sier |= SSI_SIER_RDMAE;
+
+ if (cpu_dai->id == IMX_DAI_AC97_1) {
+ __raw_writel(srccr, SSI1_SRCCR);
+ __raw_writel(sier, SSI1_SIER);
+ } else {
+ __raw_writel(srccr, SSI2_SRCCR);
+ __raw_writel(sier, SSI2_SIER);
+ }
+ return 0;
+}
+
+static int imx_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ u32 rate = params_rate(params);
+ u32 sacnt;
+
+ if (cpu_dai->id == IMX_DAI_AC97_1)
+ sacnt = __raw_readl(SSI1_SACNT);
+ else
+ sacnt = __raw_readl(SSI2_SACNT);
+
+ switch (rate) {
+ case 8000:
+ sacnt |= SSI_SACNT_FV | SSI_SACNT_FRDIV(0x05);
+ break;
+ case 11050:
+ sacnt |= SSI_SACNT_FV | SSI_SACNT_FRDIV(0x03);
+ break;
+ case 16000:
+ sacnt |= SSI_SACNT_FV | SSI_SACNT_FRDIV(0x02);
+ break;
+ case 22050:
+ sacnt |= SSI_SACNT_FV | SSI_SACNT_FRDIV(0x01);
+ break;
+ case 44100:
+ case 48000:
+ sacnt |= SSI_SACNT_FV | SSI_SACNT_FRDIV(0x00);
+ break;
+ }
+
+ if (cpu_dai->id == IMX_DAI_AC97_1)
+ __raw_writel(sacnt, SSI1_SACNT);
+ else
+ __raw_writel(sacnt, SSI2_SACNT);
+
+ /* Tx/Rx config */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return imx_ac97_hw_tx_params(substream, params, cpu_dai);
+ else
+ return imx_ac97_hw_rx_params(substream, params, cpu_dai);
+}
+
+static int imx_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 scr, i, reg;
+
+ if (cpu_dai->id == IMX_DAI_AC97_1)
+ scr = __raw_readl(SSI1_SCR);
+ else
+ scr = __raw_readl(SSI2_SCR);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < 12; i++)
+ __raw_writel(0x0, SSI1_STX0);
+ } else {
+ for (i = 0; i < 12; i++)
+ reg = __raw_readl(SSI1_SRX0);
+ }
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ imx_ac97_para.playback = 1;
+ else
+ imx_ac97_para.capture = 1;
+ scr |= SSI_SCR_TE | SSI_SCR_RE;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ imx_ac97_para.playback = 0;
+ else
+ imx_ac97_para.capture = 0;
+
+ if ((imx_ac97_para.playback == 0)
+ && (imx_ac97_para.capture == 0))
+ scr &= ~(SSI_SCR_TE | SSI_SCR_RE);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ __raw_writel(scr, SSI1_SCR);
+ else
+ __raw_writel(scr, SSI2_SCR);
+ SSI_DUMP();
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int imx_ac97_suspend(struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to disable any clocks? */
+
+ return 0;
+}
+
+static int imx_ac97_resume(struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to enable any clocks? */
+
+ return 0;
+}
+#else
+#define imx_ac97_suspend NULL
+#define imx_ac97_resume NULL
+#endif
+
+static irqreturn_t ssi1_irq(int irq, void *dev_id)
+{
+ u32 sier, sisr, reg;
+ sier = __raw_readl(SSI1_SIER);
+ sisr = __raw_readl(SSI1_SISR);
+ if ((sier & SSI_SIER_CMDDU_EN) && (sisr & SSI_SISR_CMDDU)) {
+ reg = __raw_readl(SSI1_SACADD);
+ reg = __raw_readl(SSI1_SACDAT);
+ complete(&ac97_completion);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ssi2_irq(int irq, void *dev_id)
+{
+ u32 sier, sisr, reg;
+ sier = __raw_readl(SSI1_SIER);
+ sisr = __raw_readl(SSI1_SISR);
+ if ((sier & SSI_SIER_CMDDU_EN) && (sisr & SSI_SISR_CMDDU)) {
+ reg = __raw_readl(SSI2_SACADD);
+ reg = __raw_readl(SSI2_SACDAT);
+ complete(&ac97_completion);
+ }
+ return IRQ_HANDLED;
+}
+
+static int imx_ac97_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+
+ u32 stccr;
+ stccr = SSI_STCCR_WL(16) | SSI_STCCR_DC(0x0C);
+
+ if (!strcmp(dai->name, "imx-ac97-1")) {
+ dai->id = IMX_DAI_AC97_1;
+ ssi1_clk = clk_get(NULL, "ssi_clk.0");
+ clk_enable(ssi1_clk);
+
+ __raw_writel(SSI_SCR_SSIEN, SSI1_SCR);
+
+ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)),
+ SSI1_SFCSR);
+
+ __raw_writel(stccr, SSI1_STCCR);
+ __raw_writel(stccr, SSI1_SRCCR);
+ __raw_writel(SSI_SACNT_AC97EN, SSI1_SACNT);
+ } else if (!strcmp(dai->name, "imx-ac97-2")) {
+ dai->id = IMX_DAI_AC97_2;
+ ssi2_clk = clk_get(NULL, "ssi_clk.1");
+ clk_enable(ssi2_clk);
+
+ __raw_writel(SSI_SCR_SSIEN, SSI2_SCR);
+
+ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)),
+ SSI2_SFCSR);
+
+ __raw_writel(stccr, SSI2_STCCR);
+ __raw_writel(stccr, SSI2_SRCCR);
+
+ __raw_writel(SSI_SACNT_AC97EN, SSI2_SACNT);
+ } else {
+ printk(KERN_ERR "%s: invalid device %s\n", __func__, dai->name);
+ return -ENODEV;
+ }
+
+ if (!strcmp(dai->name, "imx-ac97-1")) {
+ if (request_irq(MXC_INT_SSI1, ssi1_irq, 0, "ssi1", dai)) {
+ printk(KERN_ERR
+ "%s: failure requesting irq %s\n",
+ __func__, "ssi1");
+ return -EBUSY;
+ }
+ }
+
+ if (!strcmp(dai->name, "imx-ac97-2")) {
+ if (request_irq(MXC_INT_SSI2, ssi2_irq, 0, "ssi2", dai)) {
+ printk(KERN_ERR
+ "%s: failure requesting irq %s\n",
+ __func__, "ssi2");
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+static void imx_ac97_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ if (!strcmp(dai->name, "imx-ac97-1"))
+ free_irq(MXC_INT_SSI1, dai);
+
+ if (!strcmp(dai->name, "imx-ac97-2"))
+ free_irq(MXC_INT_SSI2, dai);
+}
+
+#define IMX_AC97_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define IMX_AC97_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+static struct snd_soc_dai_ops imx_ac97_dai_ops = {
+ .hw_params = imx_ac97_hw_params,
+ .trigger = imx_ac97_trigger,
+};
+
+struct snd_soc_dai imx_ac97_dai[] = {
+ {
+ .name = "imx-ac97-1",
+ .id = 0,
+ .ac97_control = 1,
+ .probe = imx_ac97_probe,
+ .remove = imx_ac97_remove,
+ .suspend = imx_ac97_suspend,
+ .resume = imx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = IMX_AC97_RATES,
+ .formats = IMX_AC97_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_AC97_RATES,
+ .formats = IMX_AC97_FORMATS,
+ },
+ .ops = &imx_ac97_dai_ops,
+ .private_data = &imx_ac97_data[IMX_DAI_AC97_1],
+ },
+ {
+ .name = "imx-ac97-2",
+ .id = 1,
+ .ac97_control = 1,
+ .probe = imx_ac97_probe,
+ .remove = imx_ac97_remove,
+ .suspend = imx_ac97_suspend,
+ .resume = imx_ac97_resume,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = IMX_AC97_RATES,
+ .formats = IMX_AC97_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_AC97_RATES,
+ .formats = IMX_AC97_FORMATS,
+ },
+ .ops = &imx_ac97_dai_ops,
+ .private_data = &imx_ac97_data[IMX_DAI_AC97_2],
+ },
+};
+EXPORT_SYMBOL_GPL(imx_ac97_dai);
+
+static int __init imx_ac97_init(void)
+{
+ return snd_soc_register_dais(imx_ac97_dai, ARRAY_SIZE(imx_ac97_dai));
+}
+
+static void __exit imx_ac97_exit(void)
+{
+ snd_soc_unregister_dais(imx_ac97_dai, ARRAY_SIZE(imx_ac97_dai));
+}
+
+module_init(imx_ac97_init);
+module_exit(imx_ac97_exit);
+
+MODULE_DESCRIPTION("i.MX ASoC AC97 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-esai.c b/sound/soc/imx/imx-esai.c
new file mode 100644
index 000000000000..2154599def5d
--- /dev/null
+++ b/sound/soc/imx/imx-esai.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+ /*!
+ * @file imx-esai.c
+ * @brief this file implements the esai interface
+ * in according to ASoC architeture
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "imx-esai.h"
+#include "imx-pcm.h"
+
+static int imx_esai_txrx_state;
+static struct imx_esai imx_esai_priv[3];
+static void __iomem *esai_ioaddr;
+
+static int imx_esai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u32 ecr, tccr, rccr;
+
+ ecr = __raw_readl(ESAI_ECR);
+ tccr = __raw_readl(ESAI_TCCR);
+ rccr = __raw_readl(ESAI_RCCR);
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ tccr &=
+ ~(ESAI_TCCR_THCKD | ESAI_TCCR_TCKD |
+ ESAI_TCCR_TFSD);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ rccr &=
+ ~(ESAI_RCCR_RHCKD | ESAI_RCCR_RCKD |
+ ESAI_RCCR_RFSD);
+ } else {
+ tccr |=
+ ESAI_TCCR_THCKD | ESAI_TCCR_TCKD | ESAI_TCCR_TFSD;
+ rccr |=
+ ESAI_RCCR_RHCKD | ESAI_RCCR_RCKD | ESAI_RCCR_RFSD;
+ if (clk_id == ESAI_CLK_FSYS) {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ ecr &= ~(ESAI_ECR_ETI | ESAI_ECR_ETO);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ ecr &= ~(ESAI_ECR_ERI | ESAI_ECR_ERO);
+ } else if (clk_id == ESAI_CLK_EXTAL) {
+ ecr |= ESAI_ECR_ETI;
+ ecr |= ESAI_ECR_ETO;
+ ecr |= ESAI_ECR_ERI;
+ ecr |= ESAI_ECR_ERO;
+ }
+ }
+
+ __raw_writel(ecr, ESAI_ECR);
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ __raw_writel(tccr, ESAI_TCCR);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ __raw_writel(rccr, ESAI_RCCR);
+
+ ESAI_DUMP();
+
+ return 0;
+}
+
+static int imx_esai_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ u32 tccr, rccr;
+
+ tccr = __raw_readl(ESAI_TCCR);
+ rccr = __raw_readl(ESAI_RCCR);
+
+ switch (div_id) {
+ case ESAI_TX_DIV_PSR:
+ tccr &= ESAI_TCCR_TPSR_MASK;
+ if (div)
+ tccr |= ESAI_TCCR_TPSR_BYPASS;
+ else
+ tccr &= ~ESAI_TCCR_TPSR_DIV8;
+ break;
+ case ESAI_TX_DIV_PM:
+ tccr &= ESAI_TCCR_TPM_MASK;
+ tccr |= ESAI_TCCR_TPM(div);
+ break;
+ case ESAI_TX_DIV_FP:
+ tccr &= ESAI_TCCR_TFP_MASK;
+ tccr |= ESAI_TCCR_TFP(div);
+ break;
+ case ESAI_RX_DIV_PSR:
+ rccr &= ESAI_RCCR_RPSR_MASK;
+ if (div)
+ rccr |= ESAI_RCCR_RPSR_BYPASS;
+ else
+ rccr &= ~ESAI_RCCR_RPSR_DIV8;
+ break;
+ case ESAI_RX_DIV_PM:
+ rccr &= ESAI_RCCR_RPM_MASK;
+ rccr |= ESAI_RCCR_RPM(div);
+ break;
+ case ESAI_RX_DIV_FP:
+ rccr &= ESAI_RCCR_RFP_MASK;
+ rccr |= ESAI_RCCR_RFP(div);
+ break;
+ return -EINVAL;
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ __raw_writel(tccr, ESAI_TCCR);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ __raw_writel(rccr, ESAI_RCCR);
+ return 0;
+}
+
+/*
+ * ESAI Network Mode or TDM slots configuration.
+ */
+static int imx_esai_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ u32 tccr, rccr;
+
+ if (dai->id & IMX_DAI_ESAI_TX) {
+ tccr = __raw_readl(ESAI_TCCR);
+
+ tccr &= ESAI_TCCR_TDC_MASK;
+ tccr |= ESAI_TCCR_TDC(slots - 1);
+
+ __raw_writel(tccr, ESAI_TCCR);
+ __raw_writel((tx_mask & 0xffff), ESAI_TSMA);
+ __raw_writel(((tx_mask >> 16) & 0xffff), ESAI_TSMB);
+ }
+
+ if (dai->id & IMX_DAI_ESAI_RX) {
+ rccr = __raw_readl(ESAI_RCCR);
+
+ rccr &= ESAI_RCCR_RDC_MASK;
+ rccr |= ESAI_RCCR_RDC(slots - 1);
+
+ __raw_writel(rccr, ESAI_RCCR);
+ __raw_writel((rx_mask & 0xffff), ESAI_RSMA);
+ __raw_writel(((rx_mask >> 16) & 0xffff), ESAI_RSMB);
+ }
+
+ ESAI_DUMP();
+
+ return 0;
+}
+
+/*
+ * ESAI DAI format configuration.
+ */
+static int imx_esai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct imx_esai *esai_mode = (struct imx_esai *)cpu_dai->private_data;
+ u32 tcr, tccr, rcr, rccr, saicr;
+
+ tcr = __raw_readl(ESAI_TCR);
+ tccr = __raw_readl(ESAI_TCCR);
+ rcr = __raw_readl(ESAI_RCR);
+ rccr = __raw_readl(ESAI_RCCR);
+ saicr = __raw_readl(ESAI_SAICR);
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* data on rising edge of bclk, frame low 1clk before data */
+ tcr &= ~ESAI_TCR_TFSL;
+ tcr |= ESAI_TCR_TFSR;
+ rcr &= ~ESAI_RCR_RFSL;
+ rcr |= ESAI_RCR_RFSR;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ tcr &= ~(ESAI_TCR_TFSL | ESAI_TCR_TFSR);
+ rcr &= ~(ESAI_RCR_RFSL | ESAI_RCR_RFSR);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ tcr |= ESAI_TCR_TFSL;
+ rcr |= ESAI_RCR_RFSL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* data on rising edge of bclk, frame high 1clk before data */
+ tcr |= ESAI_TCR_TFSL;
+ rcr |= ESAI_RCR_RFSL;
+ break;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ tccr |= ESAI_TCCR_TFSP;
+ tccr &= ~(ESAI_TCCR_TCKP | ESAI_TCCR_THCKP);
+ rccr &= ~(ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP);
+ rccr |= ESAI_RCCR_RFSP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ tccr &= ~(ESAI_TCCR_TCKP | ESAI_TCCR_THCKP | ESAI_TCCR_TFSP);
+ rccr &= ~(ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP | ESAI_RCCR_RFSP);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ tccr |= ESAI_TCCR_TCKP | ESAI_TCCR_THCKP | ESAI_TCCR_TFSP;
+ rccr |= ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP | ESAI_RCCR_RFSP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ tccr &= ~ESAI_TCCR_TFSP;
+ tccr |= ESAI_TCCR_TCKP | ESAI_TCCR_THCKP;
+ rccr &= ~ESAI_RCCR_RFSP;
+ rccr |= ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ tccr &= ~(ESAI_TCCR_TFSD | ESAI_TCCR_TCKD);
+ rccr &= ~(ESAI_RCCR_RFSD | ESAI_RCCR_RCKD);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ tccr &= ~ESAI_TCCR_TFSD;
+ tccr |= ESAI_TCCR_TCKD;
+ rccr &= ~ESAI_RCCR_RFSD;
+ rccr |= ESAI_RCCR_RCKD;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ tccr &= ~ESAI_TCCR_TCKD;
+ tccr |= ESAI_TCCR_TFSD;
+ rccr &= ~ESAI_RCCR_RCKD;
+ rccr |= ESAI_RCCR_RFSD;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ tccr |= (ESAI_TCCR_TFSD | ESAI_TCCR_TCKD);
+ rccr |= (ESAI_RCCR_RFSD | ESAI_RCCR_RCKD);
+ }
+
+ /* sync */
+ if (esai_mode->sync_mode)
+ saicr |= ESAI_SAICR_SYNC;
+ else
+ saicr &= ~ESAI_SAICR_SYNC;
+
+ tcr &= ESAI_TCR_TMOD_MASK;
+ rcr &= ESAI_RCR_RMOD_MASK;
+ if (esai_mode->network_mode) {
+ tcr |= ESAI_TCR_TMOD_NETWORK;
+ rcr |= ESAI_RCR_RMOD_NETWORK;
+ } else {
+ tcr |= ESAI_TCR_TMOD_NORMAL;
+ rcr |= ESAI_RCR_RMOD_NORMAL;
+ }
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ __raw_writel(tcr, ESAI_TCR);
+ __raw_writel(tccr, ESAI_TCCR);
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ __raw_writel(rcr, ESAI_RCR);
+ __raw_writel(rccr, ESAI_RCCR);
+ }
+
+ __raw_writel(saicr, ESAI_SAICR);
+
+ ESAI_DUMP();
+ return 0;
+}
+
+static struct clk *esai_clk;
+
+static int fifo_err_counter;
+
+static irqreturn_t esai_irq(int irq, void *dev_id)
+{
+ if (fifo_err_counter++ % 1000 == 0)
+ printk(KERN_ERR
+ "esai_irq SAISR %x fifo_errs=%d\n",
+ __raw_readl(ESAI_SAISR), fifo_err_counter);
+ return IRQ_HANDLED;
+}
+
+static int imx_esai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ if (cpu_dai->playback.active && (cpu_dai->id & IMX_DAI_ESAI_TX))
+ return 0;
+ if (cpu_dai->capture.active && (cpu_dai->id & IMX_DAI_ESAI_RX))
+ return 0;
+
+ if (!(imx_esai_txrx_state & IMX_DAI_ESAI_TXRX)) {
+ if (request_irq(MXC_INT_ESAI, esai_irq, 0, "esai", NULL)) {
+ pr_err("%s: failure requesting esai irq\n", __func__);
+ return -EBUSY;
+ }
+ clk_enable(esai_clk);
+ __raw_writel(ESAI_ECR_ERST, ESAI_ECR);
+ __raw_writel(ESAI_ECR_ESAIEN, ESAI_ECR);
+
+ __raw_writel(ESAI_GPIO_ESAI, ESAI_PRRC);
+ __raw_writel(ESAI_GPIO_ESAI, ESAI_PCRC);
+ }
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ imx_esai_txrx_state |= IMX_DAI_ESAI_TX;
+ __raw_writel(ESAI_TCR_TPR, ESAI_TCR);
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ imx_esai_txrx_state |= IMX_DAI_ESAI_RX;
+ __raw_writel(ESAI_RCR_RPR, ESAI_RCR);
+ }
+
+ ESAI_DUMP();
+ return 0;
+}
+
+/*
+ * This function is called to initialize the TX port before enable
+ * the tx port.
+ */
+static int imx_esai_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u32 tcr, tfcr;
+ unsigned int channels;
+
+ tcr = __raw_readl(ESAI_TCR);
+ tfcr = __raw_readl(ESAI_TFCR);
+
+ tfcr |= ESAI_TFCR_TFR;
+ __raw_writel(tfcr, ESAI_TFCR);
+ tfcr &= ~ESAI_TFCR_TFR;
+ /* DAI data (word) size */
+ tfcr &= ESAI_TFCR_TWA_MASK;
+ tcr &= ESAI_TCR_TSWS_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tfcr |= ESAI_WORD_LEN_16;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ tfcr |= ESAI_WORD_LEN_20;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ tfcr |= ESAI_WORD_LEN_24;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL24;
+ break;
+ }
+
+ channels = params_channels(params);
+ tfcr &= ESAI_TFCR_TE_MASK;
+ tfcr |= ESAI_TFCR_TE(channels);
+
+ tfcr |= ESAI_TFCR_TFWM(64);
+
+ /* Left aligned, Zero padding */
+ tcr |= ESAI_TCR_PADC;
+
+ __raw_writel(tcr, ESAI_TCR);
+ __raw_writel(tfcr, ESAI_TFCR);
+
+ ESAI_DUMP();
+ return 0;
+}
+
+/*
+ * This function is called to initialize the RX port before enable
+ * the rx port.
+ */
+static int imx_esai_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u32 rcr, rfcr;
+ unsigned int channels;
+
+ rcr = __raw_readl(ESAI_RCR);
+ rfcr = __raw_readl(ESAI_RFCR);
+
+ rfcr |= ESAI_RFCR_RFR;
+ __raw_writel(rfcr, ESAI_RFCR);
+ rfcr &= ~ESAI_RFCR_RFR;
+
+ rfcr &= ESAI_RFCR_RWA_MASK;
+ rcr &= ESAI_RCR_RSWS_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rfcr |= ESAI_WORD_LEN_16;
+ rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL32_WDL16;
+ break;
+ }
+
+ channels = params_channels(params);
+ rfcr &= ESAI_RFCR_RE_MASK;
+ rfcr |= ESAI_RFCR_RE(channels);
+
+ rfcr |= ESAI_RFCR_RFWM(64);
+
+ __raw_writel(rcr, ESAI_RCR);
+ __raw_writel(rfcr, ESAI_RFCR);
+ return 0;
+}
+
+/*
+ * This function is called to initialize the TX or RX port,
+ */
+static int imx_esai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ /* Tx/Rx config */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (__raw_readl(ESAI_TCR) & ESAI_TCR_TE0)
+ return 0;
+ return imx_esai_hw_tx_params(substream, params, dai);
+ } else {
+ if (__raw_readl(ESAI_RCR) & ESAI_RCR_RE1)
+ return 0;
+ return imx_esai_hw_rx_params(substream, params, dai);
+ }
+}
+
+static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ u32 reg, tfcr = 0, rfcr = 0;
+ u32 temp;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tfcr = __raw_readl(ESAI_TFCR);
+ reg = __raw_readl(ESAI_TCR);
+ } else {
+ rfcr = __raw_readl(ESAI_RFCR);
+ reg = __raw_readl(ESAI_RCR);
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tfcr |= ESAI_TFCR_TFEN;
+ __raw_writel(tfcr, ESAI_TFCR);
+ reg &= ~ESAI_TCR_TPR;
+ reg |= ESAI_TCR_TE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_TCR);
+ } else {
+ temp = __raw_readl(ESAI_TCR);
+ temp &= ~ESAI_TCR_TPR;
+ __raw_writel(temp, ESAI_TCR);
+ rfcr |= ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
+ reg &= ~ESAI_RCR_RPR;
+ reg |= ESAI_RCR_RE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_RCR);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg &= ~ESAI_TCR_TE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_TCR);
+ reg |= ESAI_TCR_TPR;
+ __raw_writel(reg, ESAI_TCR);
+ tfcr |= ESAI_TFCR_TFR;
+ tfcr &= ~ESAI_TFCR_TFEN;
+ __raw_writel(tfcr, ESAI_TFCR);
+ tfcr &= ~ESAI_TFCR_TFR;
+ __raw_writel(tfcr, ESAI_TFCR);
+ } else {
+ reg &= ~ESAI_RCR_RE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_RCR);
+ reg |= ESAI_RCR_RPR;
+ __raw_writel(reg, ESAI_RCR);
+ rfcr |= ESAI_RFCR_RFR;
+ rfcr &= ~ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
+ rfcr &= ~ESAI_RFCR_RFR;
+ __raw_writel(rfcr, ESAI_RFCR);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ ESAI_DUMP();
+ return 0;
+}
+
+static void imx_esai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (dai->id & IMX_DAI_ESAI_TX)
+ imx_esai_txrx_state &= ~IMX_DAI_ESAI_TX;
+ if (dai->id & IMX_DAI_ESAI_RX)
+ imx_esai_txrx_state &= ~IMX_DAI_ESAI_RX;
+
+ /* shutdown ESAI if neither Tx or Rx is active */
+ if (!(imx_esai_txrx_state & IMX_DAI_ESAI_TXRX)) {
+ free_irq(MXC_INT_ESAI, NULL);
+ clk_disable(esai_clk);
+ }
+}
+
+#ifdef CONFIG_PM
+static int imx_esai_suspend(struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /*do we need to disable any clocks */
+ return 0;
+}
+
+static int imx_esai_resume(struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to enable any clocks */
+ return 0;
+}
+
+#else
+#define imx_esai_suspend NULL
+#define imx_esai_resume NULL
+#endif
+
+static int imx_esai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ imx_esai_txrx_state = 0;
+
+ esai_clk = clk_get(NULL, "esai_clk");
+ if (!esai_clk)
+ printk(KERN_WARNING "Can't get the clock esai_clk \n");
+ return 0;
+}
+
+static void imx_esai_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+
+ clk_put(esai_clk);
+}
+
+#define IMX_ESAI_RATES SNDRV_PCM_RATE_8000_192000
+
+#define IMX_ESAI_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops imx_esai_dai_ops = {
+ .startup = imx_esai_startup,
+ .shutdown = imx_esai_shutdown,
+ .trigger = imx_esai_trigger,
+ .hw_params = imx_esai_hw_params,
+ .set_sysclk = imx_esai_set_dai_sysclk,
+ .set_clkdiv = imx_esai_set_dai_clkdiv,
+ .set_fmt = imx_esai_set_dai_fmt,
+ .set_tdm_slot = imx_esai_set_dai_tdm_slot,
+};
+
+struct snd_soc_dai imx_esai_dai[] = {
+ {
+ .name = "imx-esai-tx",
+ .id = IMX_DAI_ESAI_TX,
+ .probe = imx_esai_probe,
+ .remove = imx_esai_remove,
+ .suspend = imx_esai_suspend,
+ .resume = imx_esai_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .ops = &imx_esai_dai_ops,
+ .private_data = &imx_esai_priv[0],
+ },
+ {
+ .name = "imx-esai-rx",
+ .id = IMX_DAI_ESAI_RX,
+ .probe = imx_esai_probe,
+ .remove = imx_esai_remove,
+ .suspend = imx_esai_suspend,
+ .resume = imx_esai_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .ops = &imx_esai_dai_ops,
+ .private_data = &imx_esai_priv[1],
+ },
+ {
+ .name = "imx-esai-txrx",
+ .id = IMX_DAI_ESAI_TXRX,
+ .probe = imx_esai_probe,
+ .remove = imx_esai_remove,
+ .suspend = imx_esai_suspend,
+ .resume = imx_esai_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .ops = &imx_esai_dai_ops,
+ .private_data = &imx_esai_priv[2],
+ },
+
+};
+
+EXPORT_SYMBOL_GPL(imx_esai_dai);
+
+static int imx_esai_dev_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct mxc_esai_platform_data *plat_data = pdev->dev.platform_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ esai_ioaddr = ioremap(res->start, res->end - res->start + 1);
+
+ if (plat_data->activate_esai_ports)
+ plat_data->activate_esai_ports();
+
+ snd_soc_register_dais(imx_esai_dai, ARRAY_SIZE(imx_esai_dai));
+ return 0;
+}
+
+static int __devexit imx_esai_dev_remove(struct platform_device *pdev)
+{
+
+ struct mxc_esai_platform_data *plat_data = pdev->dev.platform_data;
+ iounmap(esai_ioaddr);
+ if (plat_data->deactivate_esai_ports)
+ plat_data->deactivate_esai_ports();
+
+ snd_soc_unregister_dais(imx_esai_dai, ARRAY_SIZE(imx_esai_dai));
+ return 0;
+}
+
+
+static struct platform_driver imx_esai_driver = {
+ .probe = imx_esai_dev_probe,
+ .remove = __devexit_p(imx_esai_dev_remove),
+ .driver = {
+ .name = "mxc_esai",
+ },
+};
+
+static int __init imx_esai_init(void)
+{
+ return platform_driver_register(&imx_esai_driver);
+}
+
+static void __exit imx_esai_exit(void)
+{
+ platform_driver_unregister(&imx_esai_driver);
+}
+
+module_init(imx_esai_init);
+module_exit(imx_esai_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX ASoC ESAI driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-esai.h b/sound/soc/imx/imx-esai.h
new file mode 100644
index 000000000000..6e2cce0eff4a
--- /dev/null
+++ b/sound/soc/imx/imx-esai.h
@@ -0,0 +1,314 @@
+/*
+ * imx-esai.h -- ESAI driver header file for Freescale IMX
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _MXC_ESAI_H
+#define _MXC_ESAI_H
+
+/*#define IMX_ESAI_DUMP 1*/
+
+#ifdef IMX_ESAI_DUMP
+#define ESAI_DUMP() \
+ do {pr_info("dump @ %s\n", __func__); \
+ pr_info("ecr %x\n", __raw_readl(ESAI_ECR)); \
+ pr_info("esr %x\n", __raw_readl(ESAI_ESR)); \
+ pr_info("tfcr %x\n", __raw_readl(ESAI_TFCR)); \
+ pr_info("tfsr %x\n", __raw_readl(ESAI_TFSR)); \
+ pr_info("rfcr %x\n", __raw_readl(ESAI_RFCR)); \
+ pr_info("rfsr %x\n", __raw_readl(ESAI_RFSR)); \
+ pr_info("tsr %x\n", __raw_readl(ESAI_TSR)); \
+ pr_info("saisr %x\n", __raw_readl(ESAI_SAISR)); \
+ pr_info("saicr %x\n", __raw_readl(ESAI_SAICR)); \
+ pr_info("tcr %x\n", __raw_readl(ESAI_TCR)); \
+ pr_info("tccr %x\n", __raw_readl(ESAI_TCCR)); \
+ pr_info("rcr %x\n", __raw_readl(ESAI_RCR)); \
+ pr_info("rccr %x\n", __raw_readl(ESAI_RCCR)); \
+ pr_info("tsma %x\n", __raw_readl(ESAI_TSMA)); \
+ pr_info("tsmb %x\n", __raw_readl(ESAI_TSMB)); \
+ pr_info("rsma %x\n", __raw_readl(ESAI_RSMA)); \
+ pr_info("rsmb %x\n", __raw_readl(ESAI_RSMB)); \
+ pr_info("prrc %x\n", __raw_readl(ESAI_PRRC)); \
+ pr_info("pcrc %x\n", __raw_readl(ESAI_PCRC)); } while (0);
+#else
+#define ESAI_DUMP()
+#endif
+
+#define ESAI_IO_BASE_ADDR (esai_ioaddr)
+
+#define ESAI_ETDR (ESAI_IO_BASE_ADDR + 0x00)
+#define ESAI_ERDR (ESAI_IO_BASE_ADDR + 0x04)
+#define ESAI_ECR (ESAI_IO_BASE_ADDR + 0x08)
+#define ESAI_ESR (ESAI_IO_BASE_ADDR + 0x0C)
+#define ESAI_TFCR (ESAI_IO_BASE_ADDR + 0x10)
+#define ESAI_TFSR (ESAI_IO_BASE_ADDR + 0x14)
+#define ESAI_RFCR (ESAI_IO_BASE_ADDR + 0x18)
+#define ESAI_RFSR (ESAI_IO_BASE_ADDR + 0x1C)
+#define ESAI_TX0 (ESAI_IO_BASE_ADDR + 0x80)
+#define ESAI_TX1 (ESAI_IO_BASE_ADDR + 0x84)
+#define ESAI_TX2 (ESAI_IO_BASE_ADDR + 0x88)
+#define ESAI_TX3 (ESAI_IO_BASE_ADDR + 0x8C)
+#define ESAI_TX4 (ESAI_IO_BASE_ADDR + 0x90)
+#define ESAI_TX5 (ESAI_IO_BASE_ADDR + 0x94)
+#define ESAI_TSR (ESAI_IO_BASE_ADDR + 0x98)
+#define ESAI_RX0 (ESAI_IO_BASE_ADDR + 0xA0)
+#define ESAI_RX1 (ESAI_IO_BASE_ADDR + 0xA4)
+#define ESAI_RX2 (ESAI_IO_BASE_ADDR + 0xA8)
+#define ESAI_RX3 (ESAI_IO_BASE_ADDR + 0xAC)
+#define ESAI_SAISR (ESAI_IO_BASE_ADDR + 0xCC)
+#define ESAI_SAICR (ESAI_IO_BASE_ADDR + 0xD0)
+#define ESAI_TCR (ESAI_IO_BASE_ADDR + 0xD4)
+#define ESAI_TCCR (ESAI_IO_BASE_ADDR + 0xD8)
+#define ESAI_RCR (ESAI_IO_BASE_ADDR + 0xDC)
+#define ESAI_RCCR (ESAI_IO_BASE_ADDR + 0xE0)
+#define ESAI_TSMA (ESAI_IO_BASE_ADDR + 0xE4)
+#define ESAI_TSMB (ESAI_IO_BASE_ADDR + 0xE8)
+#define ESAI_RSMA (ESAI_IO_BASE_ADDR + 0xEC)
+#define ESAI_RSMB (ESAI_IO_BASE_ADDR + 0xF0)
+#define ESAI_PRRC (ESAI_IO_BASE_ADDR + 0xF8)
+#define ESAI_PCRC (ESAI_IO_BASE_ADDR + 0xFC)
+
+#define ESAI_ECR_ETI (1 << 19)
+#define ESAI_ECR_ETO (1 << 18)
+#define ESAI_ECR_ERI (1 << 17)
+#define ESAI_ECR_ERO (1 << 16)
+#define ESAI_ECR_ERST (1 << 1)
+#define ESAI_ECR_ESAIEN (1 << 0)
+
+#define ESAI_ESR_TINIT (1 << 10)
+#define ESAI_ESR_RFF (1 << 9)
+#define ESAI_ESR_TFE (1 << 8)
+#define ESAI_ESR_TLS (1 << 7)
+#define ESAI_ESR_TDE (1 << 6)
+#define ESAI_ESR_TED (1 << 5)
+#define ESAI_ESR_TD (1 << 4)
+#define ESAI_ESR_RLS (1 << 3)
+#define ESAI_ESR_RDE (1 << 2)
+#define ESAI_ESR_RED (1 << 1)
+#define ESAI_ESR_RD (1 << 0)
+
+#define ESAI_TFCR_TIEN (1 << 19)
+#define ESAI_TFCR_TE5 (1 << 7)
+#define ESAI_TFCR_TE4 (1 << 6)
+#define ESAI_TFCR_TE3 (1 << 5)
+#define ESAI_TFCR_TE2 (1 << 4)
+#define ESAI_TFCR_TE1 (1 << 3)
+#define ESAI_TFCR_TE0 (1 << 2)
+#define ESAI_TFCR_TFR (1 << 1)
+#define ESAI_TFCR_TFEN (1 << 0)
+#define ESAI_TFCR_TE(x) ((0x3f >> (6 - ((x + 1) >> 1))) << 2)
+#define ESAI_TFCR_TE_MASK 0xfff03
+#define ESAI_TFCR_TFWM(x) ((x - 1) << 8)
+#define ESAI_TFCR_TWA_MASK 0xf8ffff
+
+#define ESAI_RFCR_REXT (1 << 19)
+#define ESAI_RFCR_RE3 (1 << 5)
+#define ESAI_RFCR_RE2 (1 << 4)
+#define ESAI_RFCR_RE1 (1 << 3)
+#define ESAI_RFCR_RE0 (1 << 2)
+#define ESAI_RFCR_RFR (1 << 1)
+#define ESAI_RFCR_RFEN (1 << 0)
+#define ESAI_RFCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 2)
+#define ESAI_RFCR_RE_MASK 0xfffc3
+#define ESAI_RFCR_RFWM(x) ((x-1) << 8)
+#define ESAI_RFCR_RWA_MASK 0xf8ffff
+
+#define ESAI_WORD_LEN_32 (0x00 << 16)
+#define ESAI_WORD_LEN_28 (0x01 << 16)
+#define ESAI_WORD_LEN_24 (0x02 << 16)
+#define ESAI_WORD_LEN_20 (0x03 << 16)
+#define ESAI_WORD_LEN_16 (0x04 << 16)
+#define ESAI_WORD_LEN_12 (0x05 << 16)
+#define ESAI_WORD_LEN_8 (0x06 << 16)
+#define ESAI_WORD_LEN_4 (0x07 << 16)
+
+#define ESAI_SAISR_TODFE (1 << 17)
+#define ESAI_SAISR_TEDE (1 << 16)
+#define ESAI_SAISR_TDE (1 << 15)
+#define ESAI_SAISR_TUE (1 << 14)
+#define ESAI_SAISR_TFS (1 << 13)
+#define ESAI_SAISR_RODF (1 << 10)
+#define ESAI_SAISR_REDF (1 << 9)
+#define ESAI_SAISR_RDF (1 << 8)
+#define ESAI_SAISR_ROE (1 << 7)
+#define ESAI_SAISR_RFS (1 << 6)
+#define ESAI_SAISR_IF2 (1 << 2)
+#define ESAI_SAISR_IF1 (1 << 1)
+#define ESAI_SAISR_IF0 (1 << 0)
+
+#define ESAI_SAICR_ALC (1 << 8)
+#define ESAI_SAICR_TEBE (1 << 7)
+#define ESAI_SAICR_SYNC (1 << 6)
+#define ESAI_SAICR_OF2 (1 << 2)
+#define ESAI_SAICR_OF1 (1 << 1)
+#define ESAI_SAICR_OF0 (1 << 0)
+
+#define ESAI_TCR_TLIE (1 << 23)
+#define ESAI_TCR_TIE (1 << 22)
+#define ESAI_TCR_TEDIE (1 << 21)
+#define ESAI_TCR_TEIE (1 << 20)
+#define ESAI_TCR_TPR (1 << 19)
+#define ESAI_TCR_PADC (1 << 17)
+#define ESAI_TCR_TFSR (1 << 16)
+#define ESAI_TCR_TFSL (1 << 15)
+#define ESAI_TCR_TWA (1 << 7)
+#define ESAI_TCR_TSHFD_MSB (0 << 6)
+#define ESAI_TCR_TSHFD_LSB (1 << 6)
+#define ESAI_TCR_TE5 (1 << 5)
+#define ESAI_TCR_TE4 (1 << 4)
+#define ESAI_TCR_TE3 (1 << 3)
+#define ESAI_TCR_TE2 (1 << 2)
+#define ESAI_TCR_TE1 (1 << 1)
+#define ESAI_TCR_TE0 (1 << 0)
+#define ESAI_TCR_TE(x) (0x3f >> (6 - ((x + 1) >> 1)))
+
+#define ESAI_TCR_TSWS_MASK 0xff83ff
+#define ESAI_TCR_TSWS_STL8_WDL8 (0x00 << 10)
+#define ESAI_TCR_TSWS_STL12_WDL8 (0x04 << 10)
+#define ESAI_TCR_TSWS_STL12_WDL12 (0x01 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL8 (0x08 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL12 (0x05 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL16 (0x02 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL8 (0x0c << 10)
+#define ESAI_TCR_TSWS_STL20_WDL12 (0x09 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL16 (0x06 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL20 (0x03 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL8 (0x10 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL12 (0x0d << 10)
+#define ESAI_TCR_TSWS_STL24_WDL16 (0x0a << 10)
+#define ESAI_TCR_TSWS_STL24_WDL20 (0x07 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL24 (0x1e << 10)
+#define ESAI_TCR_TSWS_STL32_WDL8 (0x18 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL12 (0x15 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL16 (0x12 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL20 (0x0f << 10)
+#define ESAI_TCR_TSWS_STL32_WDL24 (0x1f << 10)
+
+#define ESAI_TCR_TMOD_MASK 0xfffcff
+#define ESAI_TCR_TMOD_NORMAL (0x00 << 8)
+#define ESAI_TCR_TMOD_ONDEMAND (0x01 << 8)
+#define ESAI_TCR_TMOD_NETWORK (0x01 << 8)
+#define ESAI_TCR_TMOD_RESERVED (0x02 << 8)
+#define ESAI_TCR_TMOD_AC97 (0x03 << 8)
+
+#define ESAI_TCCR_THCKD (1 << 23)
+#define ESAI_TCCR_TFSD (1 << 22)
+#define ESAI_TCCR_TCKD (1 << 21)
+#define ESAI_TCCR_THCKP (1 << 20)
+#define ESAI_TCCR_TFSP (1 << 19)
+#define ESAI_TCCR_TCKP (1 << 18)
+
+#define ESAI_TCCR_TPSR_MASK 0xfffeff
+#define ESAI_TCCR_TPSR_BYPASS (1 << 8)
+#define ESAI_TCCR_TPSR_DIV8 (0 << 8)
+
+#define ESAI_TCCR_TFP_MASK 0xfc3fff
+#define ESAI_TCCR_TFP(x) ((x & 0xf) << 14)
+
+#define ESAI_TCCR_TDC_MASK 0xffc1ff
+#define ESAI_TCCR_TDC(x) (((x) & 0x1f) << 9)
+
+#define ESAI_TCCR_TPM_MASK 0xffff00
+#define ESAI_TCCR_TPM(x) (x & 0xff)
+
+#define ESAI_RCR_RLIE (1 << 23)
+#define ESAI_RCR_RIE (1 << 22)
+#define ESAI_RCR_REDIE (1 << 21)
+#define ESAI_RCR_REIE (1 << 20)
+#define ESAI_RCR_RPR (1 << 19)
+#define ESAI_RCR_RFSR (1 << 16)
+#define ESAI_RCR_RFSL (1 << 15)
+#define ESAI_RCR_RWA (1 << 7)
+#define ESAI_RCR_RSHFD_MSB (0 << 6)
+#define ESAI_RCR_RSHFD_LSB (1 << 6)
+#define ESAI_RCR_RE3 (1 << 3)
+#define ESAI_RCR_RE2 (1 << 2)
+#define ESAI_RCR_RE1 (1 << 1)
+#define ESAI_RCR_RE0 (1 << 0)
+#define ESAI_RCR_RE(x) (0xf >> (4 - ((x + 1) >> 1)))
+
+#define ESAI_RCR_RSWS_MASK 0xff83ff
+#define ESAI_RCR_RSWS_STL8_WDL8 (0x00 << 10)
+#define ESAI_RCR_RSWS_STL12_WDL8 (0x04 << 10)
+#define ESAI_RCR_RSWS_STL12_WDL12 (0x01 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL8 (0x08 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL12 (0x05 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL16 (0x02 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL8 (0x0c << 10)
+#define ESAI_RCR_RSWS_STL20_WDL12 (0x09 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL16 (0x06 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL20 (0x03 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL8 (0x10 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL12 (0x0d << 10)
+#define ESAI_RCR_RSWS_STL24_WDL16 (0x0a << 10)
+#define ESAI_RCR_RSWS_STL24_WDL20 (0x07 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL24 (0x1e << 10)
+#define ESAI_RCR_RSWS_STL32_WDL8 (0x18 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL12 (0x15 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL16 (0x12 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL20 (0x0f << 10)
+#define ESAI_RCR_RSWS_STL32_WDL24 (0x1f << 10)
+
+#define ESAI_RCR_RMOD_MASK 0xfffcff
+#define ESAI_RCR_RMOD_NORMAL (0x00 << 8)
+#define ESAI_RCR_RMOD_ONDEMAND (0x01 << 8)
+#define ESAI_RCR_RMOD_NETWORK (0x01 << 8)
+#define ESAI_RCR_RMOD_RESERVED (0x02 << 8)
+#define ESAI_RCR_RMOD_AC97 (0x03 << 8)
+
+#define ESAI_RCCR_RHCKD (1 << 23)
+#define ESAI_RCCR_RFSD (1 << 22)
+#define ESAI_RCCR_RCKD (1 << 21)
+#define ESAI_RCCR_RHCKP (1 << 20)
+#define ESAI_RCCR_RFSP (1 << 19)
+#define ESAI_RCCR_RCKP (1 << 18)
+
+#define ESAI_RCCR_RPSR_MASK 0xfffeff
+#define ESAI_RCCR_RPSR_BYPASS (1 << 8)
+#define ESAI_RCCR_RPSR_DIV8 (0 << 8)
+
+#define ESAI_RCCR_RFP_MASK 0xfc3fff
+#define ESAI_RCCR_RFP(x) ((x & 0xf) << 14)
+
+#define ESAI_RCCR_RDC_MASK 0xffc1ff
+#define ESAI_RCCR_RDC(x) (((x) & 0x1f) << 9)
+
+#define ESAI_RCCR_RPM_MASK 0xffff00
+#define ESAI_RCCR_RPM(x) (x & 0xff)
+
+#define ESAI_GPIO_ESAI 0xfff
+
+/* ESAI clock source */
+#define ESAI_CLK_FSYS 0
+#define ESAI_CLK_EXTAL 1
+
+/* ESAI clock divider */
+#define ESAI_TX_DIV_PSR 0
+#define ESAI_TX_DIV_PM 1
+#define ESAI_TX_DIV_FP 2
+#define ESAI_RX_DIV_PSR 3
+#define ESAI_RX_DIV_PM 4
+#define ESAI_RX_DIV_FP 5
+
+#define IMX_DAI_ESAI_TX 0x04
+#define IMX_DAI_ESAI_RX 0x08
+#define IMX_DAI_ESAI_TXRX (IMX_DAI_ESAI_TX | IMX_DAI_ESAI_RX)
+
+struct imx_esai {
+ bool network_mode;
+ bool sync_mode;
+};
+
+extern struct snd_soc_dai imx_esai_dai[];
+
+#endif
diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c
new file mode 100644
index 000000000000..142e584ddf22
--- /dev/null
+++ b/sound/soc/imx/imx-pcm.c
@@ -0,0 +1,719 @@
+/*
+ * imx-pcm.c -- ALSA SoC interface for the Freescale i.MX3 CPU's
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Copyright (C) 2006-2010 Freescale Semiconductor, Inc.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * Based on imx31-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
+ *
+ * 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.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/iram_alloc.h>
+#include <linux/fsl_devices.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+#include "imx-esai.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/delay.h>
+#include <linux/mxc_asrc.h>
+#endif
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+static bool UseIram = 1;
+#else
+static bool UseIram;
+#endif
+
+/* debug */
+#define IMX_PCM_DEBUG 0
+#if IMX_PCM_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+static const struct snd_pcm_hardware imx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .buffer_bytes_max = SND_RAM_SIZE,
+ .period_bytes_max = SND_RAM_SIZE / 4,
+#else
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_max = 16 * 1024,
+#endif
+ .period_bytes_min = 2 * SZ_1K,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static struct vm_operations_struct snd_mxc_audio_playback_vm_ops = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+/*
+ enable user space access to iram buffer
+*/
+static int imx_iram_audio_playback_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ unsigned long off;
+ unsigned long phys;
+ unsigned long size;
+ int ret = 0;
+
+ area->vm_ops = &snd_mxc_audio_playback_vm_ops;
+ area->vm_private_data = substream;
+
+ off = area->vm_pgoff << PAGE_SHIFT;
+ phys = buf->addr + off;
+ size = area->vm_end - area->vm_start;
+
+ if (off + size > SND_RAM_SIZE)
+ return -EINVAL;
+
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ area->vm_flags |= VM_IO;
+ ret =
+ remap_pfn_range(area, area->vm_start, phys >> PAGE_SHIFT,
+ size, area->vm_page_prot);
+
+ return ret;
+}
+
+static int imx_get_sdma_transfer(int format, int dai_port,
+ struct snd_pcm_substream *substream)
+{
+ int transfer = -1;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ if (prtd->asrc_enable == 1) {
+ if (dai_port == IMX_DAI_SSI0) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI1_TX0;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI1_TX0;
+ } else if (dai_port == IMX_DAI_SSI1) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI1_TX1;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI1_TX1;
+ } else if (dai_port == IMX_DAI_SSI2) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI2_TX0;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI2_TX0;
+ } else if (dai_port == IMX_DAI_SSI3) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI2_TX1;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI2_TX1;
+ } else if (dai_port & IMX_DAI_ESAI_TX) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_ESAI;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_ESAI;
+ else
+ transfer = MXC_DMA_ASRCC_ESAI;
+ }
+ } else {
+#endif
+
+ if (dai_port == IMX_DAI_SSI0) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX0;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX0;
+ }
+ } else if (dai_port == IMX_DAI_SSI1) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX1;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX1;
+ }
+ } else if (dai_port == IMX_DAI_SSI2) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX0;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX0;
+ }
+ } else if (dai_port == IMX_DAI_SSI3) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX1;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX1;
+ }
+ } else if (dai_port == IMX_DAI_SSI4) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI3_16BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI3_24BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI3_24BIT_TX0;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI3_16BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI3_24BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI3_24BIT_RX0;
+ }
+ } else if (dai_port == IMX_DAI_SSI5) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI3_16BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI3_24BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI3_24BIT_TX1;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI3_16BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI3_24BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI3_24BIT_RX1;
+ }
+ } else if ((dai_port & IMX_DAI_ESAI_TX)
+ || (dai_port & IMX_DAI_ESAI_RX)) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_ESAI_16BIT_TX;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_ESAI_24BIT_TX;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_ESAI_24BIT_TX;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_ESAI_16BIT_RX;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_ESAI_24BIT_RX;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_ESAI_24BIT_RX;
+ }
+ }
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ }
+#endif
+ return transfer;
+}
+
+static int dma_new_period(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
+ unsigned int offset = dma_size * prtd->period;
+ int ret = 0;
+ mxc_dma_requestbuf_t sdma_request;
+
+ if (!prtd->active)
+ return 0;
+
+ memset(&sdma_request, 0, sizeof(mxc_dma_requestbuf_t));
+
+ dbg("period pos ALSA %x DMA %x\n", runtime->periods, prtd->period);
+ dbg("period size ALSA %x DMA %x Offset %x dmasize %x\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes, offset, dma_size);
+ dbg("DMA addr %x\n", runtime->dma_addr + offset);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sdma_request.src_addr =
+ (dma_addr_t) (runtime->dma_addr + offset);
+ else
+ sdma_request.dst_addr =
+ (dma_addr_t) (runtime->dma_addr + offset);
+
+ sdma_request.num_of_bytes = dma_size;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mxc_dma_config(prtd->dma_wchannel,
+ &sdma_request, 1, MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(prtd->dma_wchannel);
+ } else {
+
+ mxc_dma_config(prtd->dma_wchannel,
+ &sdma_request, 1, MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(prtd->dma_wchannel);
+ }
+ prtd->dma_active = 1;
+ prtd->period++;
+ prtd->period %= runtime->periods;
+
+ return ret;
+}
+
+static void audio_dma_irq(void *data)
+{
+ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ prtd->dma_active = 0;
+ prtd->periods++;
+ prtd->periods %= runtime->periods;
+
+ dbg("irq per %d offset %x\n", prtd->periods,
+ frames_to_bytes(runtime, runtime->period_size) * prtd->periods);
+
+ if (prtd->active)
+ snd_pcm_period_elapsed(substream);
+ dma_new_period(substream);
+}
+
+static int imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ int ret = 0, channel = 0;
+
+ if (prtd->dma_alloc) {
+ mxc_dma_free(prtd->dma_wchannel);
+ prtd->dma_alloc = 0;
+ }
+
+ /* only allocate the DMA chn once */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ struct dma_channel_info info;
+ mxc_dma_requestbuf_t sdma_request;
+ info.asrc.channs = runtime->channels;
+ if (prtd->dma_asrc) {
+ mxc_dma_free(prtd->dma_asrc);
+ prtd->dma_asrc = 0;
+ }
+ memset(&sdma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ /* num_of_bytes can be set any value except for zero */
+ sdma_request.num_of_bytes = 0x40;
+ channel =
+ mxc_dma_request_ext(prtd->dma_ch,
+ "ALSA TX SDMA", &info);
+
+ mxc_dma_config(channel, &sdma_request,
+ 1, MXC_DMA_MODE_WRITE);
+ prtd->dma_asrc = channel;
+ if (prtd->asrc_index == 0)
+ prtd->dma_ch = MXC_DMA_ASRC_A_RX;
+ else if (prtd->asrc_index == 1)
+ prtd->dma_ch = MXC_DMA_ASRC_B_RX;
+ else
+ prtd->dma_ch = MXC_DMA_ASRC_C_RX;
+
+ channel =
+ mxc_dma_request(MXC_DMA_ASRC_A_RX, "ALSA ASRC RX");
+ } else
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA TX SDMA");
+#else
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA TX SDMA");
+#endif
+ if (channel < 0) {
+ pr_err("imx-pcm: error requesting \
+ a write dma channel\n");
+ return channel;
+ }
+ ret = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ audio_dma_irq, (void *)substream);
+
+ } else {
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA RX SDMA");
+ if (channel < 0) {
+ pr_err("imx-pcm: error requesting \
+ a read dma channel\n");
+ return channel;
+ }
+ ret = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ audio_dma_irq, (void *)substream);
+ }
+ prtd->dma_wchannel = channel;
+ prtd->dma_alloc = 1;
+
+ prtd->period = 0;
+ prtd->periods = 0;
+ return 0;
+}
+
+static int imx_pcm_hw_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ prtd->dma_ch =
+ imx_get_sdma_transfer(params_format(params),
+ rtd->dai->cpu_dai->id, substream);
+
+ if (prtd->dma_ch < 0) {
+ printk(KERN_ERR "imx-pcm: invaild sdma transfer type");
+ return -1;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int imx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ if (prtd->dma_wchannel) {
+ mxc_dma_free(prtd->dma_wchannel);
+ prtd->dma_wchannel = 0;
+ prtd->dma_alloc = 0;
+ }
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if ((prtd->asrc_enable == 1) && prtd->dma_asrc) {
+ mxc_dma_free(prtd->dma_asrc);
+ prtd->dma_asrc = 0;
+ }
+#endif
+
+ return 0;
+}
+
+static int imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ prtd->dma_active = 0;
+ prtd->active = 1;
+ ret = dma_new_period(substream);
+ ret = dma_new_period(substream);
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ ret = mxc_dma_enable(prtd->dma_asrc);
+ asrc_start_conv(prtd->asrc_index);
+ /* There is underrun, if immediately enable SSI after
+ start ASRC */
+ mdelay(1);
+ }
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->active = 0;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ mxc_dma_disable(prtd->dma_asrc);
+ asrc_stop_conv(prtd->asrc_index);
+ }
+#endif
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t imx_pcm_pointer(struct
+ snd_pcm_substream
+ *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ unsigned int offset = 0;
+
+ offset = (runtime->period_size * (prtd->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ dbg("pointer offset %x\n", offset);
+
+ return offset;
+}
+
+static int imx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &imx_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ return 0;
+}
+
+static int imx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static int
+imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->card->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data;
+ int ext_ram = 0;
+ int ret = 0;
+
+ dbg("+imx_pcm_mmap:"
+ "UseIram=%d dma_addr=%x dma_area=%x dma_bytes=%d\n",
+ UseIram, (unsigned int)runtime->dma_addr,
+ runtime->dma_area, runtime->dma_bytes);
+
+ if (cpu_dai->dev && cpu_dai->dev->platform_data) {
+ dev_data = cpu_dai->dev->platform_data;
+ ext_ram = dev_data->ext_ram;
+ }
+
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ || ext_ram || !UseIram) {
+ ret =
+ dma_mmap_writecombine(substream->pcm->card->
+ dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return ret;
+ } else
+ return imx_iram_audio_playback_mmap(substream, vma);
+}
+
+struct snd_pcm_ops imx_pcm_ops = {
+ .open = imx_pcm_open,
+ .close = imx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = imx_pcm_hw_params,
+ .hw_free = imx_pcm_hw_free,
+ .prepare = imx_pcm_prepare,
+ .trigger = imx_pcm_trigger,
+ .pointer = imx_pcm_pointer,
+ .mmap = imx_pcm_mmap,
+};
+
+static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->card->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data;
+ unsigned long buf_paddr;
+ int ext_ram = 0;
+ size_t size = imx_pcm_hardware.buffer_bytes_max;
+
+ if (cpu_dai->dev && cpu_dai->dev->platform_data) {
+ dev_data = cpu_dai->dev->platform_data;
+ ext_ram = dev_data->ext_ram;
+ }
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ if ((stream == SNDRV_PCM_STREAM_CAPTURE) || ext_ram || !UseIram)
+ buf->area =
+ dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ else {
+ buf->area = iram_alloc(size, &buf_paddr);
+ buf->addr = buf_paddr;
+
+ if (!buf->area) {
+ pr_warning("imx-pcm: Falling back to external ram.\n");
+ UseIram = 0;
+ buf->area =
+ dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ }
+ }
+
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ printk(KERN_INFO "DMA Sound Buffers Allocated:"
+ "UseIram=%d buf->addr=%x buf->area=%p size=%d\n",
+ UseIram, buf->addr, buf->area, size);
+ return 0;
+}
+
+static void imx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->card->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data;
+ int ext_ram = 0;
+ int stream;
+
+ if (cpu_dai->dev && cpu_dai->dev->platform_data) {
+ dev_data = cpu_dai->dev->platform_data;
+ ext_ram = dev_data->ext_ram;
+ }
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ if ((stream == SNDRV_PCM_STREAM_CAPTURE)
+ || ext_ram || !UseIram)
+ dma_free_writecombine(pcm->card->dev,
+ buf->bytes, buf->area, buf->addr);
+ else {
+ iram_free(buf->addr, imx_pcm_hardware.buffer_bytes_max);
+ }
+ buf->area = NULL;
+ }
+}
+
+static u64 imx_pcm_dmamask = 0xffffffff;
+
+static int imx_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &imx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform imx_soc_platform = {
+ .name = "imx-audio",
+ .pcm_ops = &imx_pcm_ops,
+ .pcm_new = imx_pcm_new,
+ .pcm_free = imx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(imx_soc_platform);
+
+static int __init imx_pcm_init(void)
+{
+ return snd_soc_register_platform(&imx_soc_platform);
+}
+module_init(imx_pcm_init);
+
+static void __exit imx_pcm_exit(void)
+{
+ snd_soc_unregister_platform(&imx_soc_platform);
+}
+module_exit(imx_pcm_exit);
+
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("Freescale i.MX3x PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h
new file mode 100644
index 000000000000..5a7efa171a71
--- /dev/null
+++ b/sound/soc/imx/imx-pcm.h
@@ -0,0 +1,83 @@
+/*
+ * imx-pcm.h :- ASoC platform header for Freescale i.MX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Copyright 2006, 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _MXC_PCM_H
+#define _MXC_PCM_H
+
+#include <mach/dma.h>
+
+/* AUDMUX regs definition */
+#define AUDMUX_IO_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define DAM_PTCR1 ((AUDMUX_IO_BASE_ADDR) + 0x00)
+#define DAM_PDCR1 ((AUDMUX_IO_BASE_ADDR) + 0x04)
+#define DAM_PTCR2 ((AUDMUX_IO_BASE_ADDR) + 0x08)
+#define DAM_PDCR2 ((AUDMUX_IO_BASE_ADDR) + 0x0C)
+#define DAM_PTCR3 ((AUDMUX_IO_BASE_ADDR) + 0x10)
+#define DAM_PDCR3 ((AUDMUX_IO_BASE_ADDR) + 0x14)
+#define DAM_PTCR4 ((AUDMUX_IO_BASE_ADDR) + 0x18)
+#define DAM_PDCR4 ((AUDMUX_IO_BASE_ADDR) + 0x1C)
+#define DAM_PTCR5 ((AUDMUX_IO_BASE_ADDR) + 0x20)
+#define DAM_PDCR5 ((AUDMUX_IO_BASE_ADDR) + 0x24)
+#define DAM_PTCR6 ((AUDMUX_IO_BASE_ADDR) + 0x28)
+#define DAM_PDCR6 ((AUDMUX_IO_BASE_ADDR) + 0x2C)
+#define DAM_PTCR7 ((AUDMUX_IO_BASE_ADDR) + 0x30)
+#define DAM_PDCR7 ((AUDMUX_IO_BASE_ADDR) + 0x34)
+#define DAM_CNMCR ((AUDMUX_IO_BASE_ADDR) + 0x38)
+#define DAM_PTCR(a) ((AUDMUX_IO_BASE_ADDR) + (a-1)*8)
+#define DAM_PDCR(a) ((AUDMUX_IO_BASE_ADDR) + 4 + (a-1)*8)
+
+#define AUDMUX_PTCR_TFSDIR (1 << 31)
+#define AUDMUX_PTCR_TFSSEL(x, y) \
+ ((x << 30) | (((y - 1) & 0x7) << 27))
+#define AUDMUX_PTCR_TCLKDIR (1 << 26)
+#define AUDMUX_PTCR_TCSEL(x, y) \
+ ((x << 25) | (((y - 1) & 0x7) << 22))
+#define AUDMUX_PTCR_RFSDIR (1 << 21)
+#define AUDMUX_PTCR_RFSSEL(x, y) \
+ ((x << 20) | (((y - 1) & 0x7) << 17))
+#define AUDMUX_PTCR_RCLKDIR (1 << 16)
+#define AUDMUX_PTCR_RCSEL(x, y) \
+ ((x << 15) | (((y - 1) & 0x7) << 12))
+#define AUDMUX_PTCR_SYN (1 << 11)
+
+#define AUDMUX_FROM_TXFS 0
+#define AUDMUX_FROM_RXFS 1
+
+#define AUDMUX_PDCR_RXDSEL(x) (((x - 1) & 0x7) << 13)
+#define AUDMUX_PDCR_TXDXEN (1 << 12)
+#define AUDMUX_PDCR_MODE(x) (((x) & 0x3) << 8)
+#define AUDMUX_PDCR_INNMASK(x) (((x) & 0xff) << 0)
+
+#define AUDMUX_CNMCR_CEN (1 << 18)
+#define AUDMUX_CNMCR_FSPOL (1 << 17)
+#define AUDMUX_CNMCR_CLKPOL (1 << 16)
+#define AUDMUX_CNMCR_CNTHI(x) (((x) & 0xff) << 8)
+#define AUDMUX_CNMCR_CNTLOW(x) (((x) & 0xff) << 0)
+
+
+struct mxc_runtime_data {
+ int dma_ch;
+ spinlock_t dma_lock;
+ int active, period, periods;
+ int dma_wchannel;
+ int dma_active;
+ int dma_alloc;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ int dma_asrc;
+ int asrc_index;
+ int asrc_enable;
+#endif
+};
+
+extern struct snd_soc_platform imx_soc_platform;
+
+#endif
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
index 80b4fee2442b..372854a75caf 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/imx/imx-ssi.c
@@ -1,81 +1,227 @@
/*
- * imx-ssi.c -- ALSA Soc Audio Layer
+ * imx-ssi.c -- SSI driver for Freescale IMX
*
- * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
*
- * This code is based on code copyrighted by Freescale,
- * Liam Girdwood, Javier Martin and probably others.
+ * Copyright (C) 2006-2010 Freescale Semiconductor, Inc.
+ * Based on mxc-alsa-mc13783
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
+ * Revision history
+ * 29th Aug 2006 Initial version.
+ *
+ * TODO:
+ * Need to rework SSI register defs when new defs go into mainline.
+ * Add support for TDM and FIFO 1.
*
- * The i.MX SSI core has some nasty limitations in AC97 mode. While most
- * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
- * one FIFO which combines all valid receive slots. We cannot even select
- * which slots we want to receive. The WM9712 with which this driver
- * was developped with always sends GPIO status data in slot 12 which
- * we receive in our (PCM-) data stream. The only chance we have is to
- * manually skip this data in the FIQ handler. With sampling rates different
- * from 48000Hz not every frame has valid receive data, so the ratio
- * between pcm data and GPIO status data changes. Our FIQ handler is not
- * able to handle this, hence this driver only works with 48000Hz sampling
- * rate.
- * Reading and writing AC97 registers is another challange. The core
- * provides us status bits when the read register is updated with *another*
- * value. When we read the same register two times (and the register still
- * contains the same value) these status bits are not set. We work
- * around this by not polling these bits but only wait a fixed delay.
- *
*/
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
#include <sound/core.h>
-#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-
-#include <mach/ssi.h>
+#include <mach/dma.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
#include <mach/hardware.h>
#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+#define SSI_STX0 (0x00)
+#define SSI_STX1 (0x04)
+#define SSI_SRX0 (0x08)
+#define SSI_SRX1 (0x0c)
+#define SSI_SCR (0x10)
+#define SSI_SISR (0x14)
+#define SSI_SIER (0x18)
+#define SSI_STCR (0x1c)
+#define SSI_SRCR (0x20)
+#define SSI_STCCR (0x24)
+#define SSI_SRCCR (0x28)
+#define SSI_SFCSR (0x2c)
+#define SSI_STR (0x30)
+#define SSI_SOR (0x34)
+#define SSI_SACNT (0x38)
+#define SSI_SACADD (0x3c)
+#define SSI_SACDAT (0x40)
+#define SSI_SATAG (0x44)
+#define SSI_STMSK (0x48)
+#define SSI_SRMSK (0x4c)
+#define SSI_SACCST (0x50)
+#define SSI_SACCEN (0x54)
+#define SSI_SACCDIS (0x58)
+
+/* debug */
+#define IMX_SSI_DEBUG 0
+#if IMX_SSI_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+#define IMX_SSI_DUMP 0
+#if IMX_SSI_DUMP
+#define SSI_DUMP() \
+ do { \
+ printk(KERN_INFO "dump @ %s\n", __func__); \
+ printk(KERN_INFO "scr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SCR), \
+ __raw_readl(ioaddr + SSI_SCR)); \
+ printk(KERN_INFO "sisr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SISR), \
+ __raw_readl(ioaddr + SSI_SISR)); \
+ printk(KERN_INFO "stcr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_STCR), \
+ __raw_readl(ioaddr + SSI_STCR)); \
+ printk(KERN_INFO "srcr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SRCR), \
+ __raw_readl(ioaddr + SSI_SRCR)); \
+ printk(KERN_INFO "stccr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_STCCR), \
+ __raw_readl(ioaddr + SSI_STCCR)); \
+ printk(KERN_INFO "srccr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SRCCR), \
+ __raw_readl(ioaddr + SSI_SRCCR)); \
+ printk(KERN_INFO "sfcsr %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SFCSR), \
+ __raw_readl(ioaddr + SSI_SFCSR)); \
+ printk(KERN_INFO "stmsk %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_STMSK), \
+ __raw_readl(ioaddr + SSI_STMSK)); \
+ printk(KERN_INFO "srmsk %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SRMSK), \
+ __raw_readl(ioaddr + SSI_SRMSK)); \
+ printk(KERN_INFO "sier %x\t, %x\n", \
+ __raw_readl(ioaddr + SSI_SIER), \
+ __raw_readl(ioaddr + SSI_SIER)); \
+ } while (0);
+#else
+#define SSI_DUMP()
+#endif
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 scr;
+
+ scr = __raw_readl(ioaddr + SSI_SCR);
+
+ if (scr & SSI_SCR_SSIEN)
+ return 0;
+
+ switch (clk_id) {
+ case IMX_SSP_SYS_CLK:
+ if (dir == SND_SOC_CLOCK_OUT)
+ scr |= SSI_SCR_SYS_CLK_EN;
+ else
+ scr &= ~SSI_SCR_SYS_CLK_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __raw_writel(scr, ioaddr + SSI_SCR);
+
+ return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 stccr, srccr;
+
+ if (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN)
+ return 0;
+
+ srccr = __raw_readl(ioaddr + SSI_SRCCR);
+ stccr = __raw_readl(ioaddr + SSI_STCCR);
+
+ switch (div_id) {
+ case IMX_SSI_TX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ case IMX_SSI_RX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ default:
+ return -EINVAL;
+ }
-#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
+ __raw_writel(stccr, ioaddr + SSI_STCCR);
+ __raw_writel(srccr, ioaddr + SSI_SRCCR);
+
+ return 0;
+}
/*
* SSI Network Mode or TDM slots configuration.
* Should only be called when port is inactive (i.e. SSIEN = 0).
*/
static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
- unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
{
- struct imx_ssi *ssi = cpu_dai->private_data;
- u32 sccr;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 stmsk, srmsk, stccr;
- sccr = readl(ssi->base + SSI_STCCR);
- sccr &= ~SSI_STCCR_DC_MASK;
- sccr |= SSI_STCCR_DC(slots - 1);
- writel(sccr, ssi->base + SSI_STCCR);
+ if (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN)
+ return 0;
+ stccr = __raw_readl(ioaddr + SSI_STCCR);
- sccr = readl(ssi->base + SSI_SRCCR);
- sccr &= ~SSI_STCCR_DC_MASK;
- sccr |= SSI_STCCR_DC(slots - 1);
- writel(sccr, ssi->base + SSI_SRCCR);
+ stmsk = tx_mask;
+ srmsk = rx_mask;
+ stccr &= ~SSI_STCCR_DC_MASK;
+ stccr |= SSI_STCCR_DC(slots - 1);
- writel(tx_mask, ssi->base + SSI_STMSK);
- writel(rx_mask, ssi->base + SSI_SRMSK);
+ __raw_writel(stmsk, ioaddr + SSI_STMSK);
+ __raw_writel(srmsk, ioaddr + SSI_SRMSK);
+ __raw_writel(stccr, ioaddr + SSI_STCCR);
+ __raw_writel(stccr, ioaddr + SSI_SRCCR);
return 0;
}
@@ -84,144 +230,231 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
* SSI DAI format configuration.
* Should only be called when port is inactive (i.e. SSIEN = 0).
* Note: We don't use the I2S modes but instead manually configure the
- * SSI for I2S because the I2S mode is only a register preset.
+ * SSI for I2S.
*/
static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
- struct imx_ssi *ssi = cpu_dai->private_data;
- u32 strcr = 0, scr;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 stcr = 0, srcr = 0, scr;
- scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+ scr = __raw_readl(ioaddr + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+ if (scr & SSI_SCR_SSIEN)
+ return 0;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
/* data on rising edge of bclk, frame low 1clk before data */
- strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
- scr |= SSI_SCR_NET;
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
break;
case SND_SOC_DAIFMT_LEFT_J:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TXBIT0;
+ stcr |= SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RXBIT0;
break;
case SND_SOC_DAIFMT_DSP_B:
/* data on rising edge of bclk, frame high with data */
- strcr |= SSI_STCR_TFSL;
+ stcr |= SSI_STCR_TFSL;
+ srcr |= SSI_SRCR_RFSL;
break;
case SND_SOC_DAIFMT_DSP_A:
/* data on rising edge of bclk, frame high 1clk before data */
- strcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+ srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
break;
}
/* DAI clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
- strcr |= SSI_STCR_TFSI;
- strcr &= ~SSI_STCR_TSCKP;
+ stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
break;
case SND_SOC_DAIFMT_IB_NF:
- strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ stcr |= SSI_STCR_TFSI;
+ stcr &= ~SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI;
+ srcr &= ~SSI_SRCR_RSCKP;
break;
case SND_SOC_DAIFMT_NB_IF:
- strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ stcr &= ~SSI_STCR_TFSI;
+ stcr |= SSI_STCR_TSCKP;
+ srcr &= ~SSI_SRCR_RFSI;
+ srcr |= SSI_SRCR_RSCKP;
break;
case SND_SOC_DAIFMT_NB_NF:
- strcr &= ~SSI_STCR_TFSI;
- strcr |= SSI_STCR_TSCKP;
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
break;
}
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ && priv->network_mode) {
+ scr &= ~SSI_SCR_I2S_MODE_MASK;
+ scr |= SSI_SCR_I2S_MODE_MSTR;
+ }
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ stcr |= SSI_STCR_TFDIR;
+ srcr |= SSI_SRCR_RFDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ stcr |= SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RXDIR;
+ break;
case SND_SOC_DAIFMT_CBM_CFM:
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ && priv->network_mode) {
+ scr &= ~SSI_SCR_I2S_MODE_MASK;
+ scr |= SSI_SCR_I2S_MODE_SLAVE;
+ }
break;
- default:
- /* Master mode not implemented, needs handling of clocks. */
- return -EINVAL;
}
- strcr |= SSI_STCR_TFEN0;
+ /* sync */
+ if (priv->sync_mode)
+ scr |= SSI_SCR_SYN;
- writel(strcr, ssi->base + SSI_STCR);
- writel(strcr, ssi->base + SSI_SRCR);
- writel(scr, ssi->base + SSI_SCR);
+ /* tdm - only for stereo atm */
+ if (priv->network_mode)
+ scr |= SSI_SCR_NET;
+#ifdef CONFIG_MXC_SSI_DUAL_FIFO
+ if (cpu_is_mx51() || cpu_is_mx53()) {
+ stcr |= SSI_STCR_TFEN1;
+ srcr |= SSI_SRCR_RFEN1;
+ scr |= SSI_SCR_TCH_EN;
+ }
+#endif
+ __raw_writel(stcr, ioaddr + SSI_STCR);
+ __raw_writel(srcr, ioaddr + SSI_SRCR);
+ __raw_writel(scr, ioaddr + SSI_SCR);
+ SSI_DUMP();
return 0;
}
-/*
- * SSI system clock configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
+static int imx_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct imx_ssi *ssi = cpu_dai->private_data;
- u32 scr;
-
- scr = readl(ssi->base + SSI_SCR);
-
- switch (clk_id) {
- case IMX_SSP_SYS_CLK:
- if (dir == SND_SOC_CLOCK_OUT)
- scr |= SSI_SCR_SYS_CLK_EN;
- else
- scr &= ~SSI_SCR_SYS_CLK_EN;
- break;
- default:
- return -EINVAL;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+
+ /* we cant really change any SSI values after SSI is enabled
+ * need to fix in software for max flexibility - lrg */
+ if (cpu_dai->playback.active || cpu_dai->capture.active)
+ return 0;
+
+ /* reset the SSI port - Sect 45.4.4 */
+ if (clk_get_usecount(priv->ssi_clk) != 0) {
+ clk_enable(priv->ssi_clk);
+ return 0;
}
- writel(scr, ssi->base + SSI_SCR);
+ __raw_writel(0, ioaddr + SSI_SCR);
+ clk_enable(priv->ssi_clk);
+
+ /* BIG FAT WARNING
+ * SDMA FIFO watermark must == SSI FIFO watermark for best results.
+ */
+ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)),
+ ioaddr + SSI_SFCSR);
+ __raw_writel(0, ioaddr + SSI_SIER);
+ SSI_DUMP();
return 0;
}
-/*
- * SSI Clock dividers
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
+static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
{
- struct imx_ssi *ssi = cpu_dai->private_data;
- u32 stccr, srccr;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 stccr, stcr, sier;
- stccr = readl(ssi->base + SSI_STCCR);
- srccr = readl(ssi->base + SSI_SRCCR);
+ stccr = __raw_readl(ioaddr + SSI_STCCR) & ~SSI_STCCR_WL_MASK;
+ stcr = __raw_readl(ioaddr + SSI_STCR);
+ sier = __raw_readl(ioaddr + SSI_SIER);
- switch (div_id) {
- case IMX_SSI_TX_DIV_2:
- stccr &= ~SSI_STCCR_DIV2;
- stccr |= div;
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stccr |= SSI_STCCR_WL(16);
break;
- case IMX_SSI_TX_DIV_PSR:
- stccr &= ~SSI_STCCR_PSR;
- stccr |= div;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ stccr |= SSI_STCCR_WL(20);
break;
- case IMX_SSI_TX_DIV_PM:
- stccr &= ~0xff;
- stccr |= SSI_STCCR_PM(div);
+ case SNDRV_PCM_FORMAT_S24_LE:
+ stccr |= SSI_STCCR_WL(24);
break;
- case IMX_SSI_RX_DIV_2:
- stccr &= ~SSI_STCCR_DIV2;
- stccr |= div;
+ }
+
+ /* enable interrupts */
+ if ((cpu_dai->id & 0x1) == 0)
+ stcr |= SSI_STCR_TFEN0;
+ else
+ stcr |= SSI_STCR_TFEN1;
+ sier |= SSI_SIER_TDMAE | SSI_SIER_TIE | SSI_SIER_TUE0_EN;
+
+ __raw_writel(stcr, ioaddr + SSI_STCR);
+ __raw_writel(stccr, ioaddr + SSI_STCCR);
+ __raw_writel(sier, ioaddr + SSI_SIER);
+
+ return 0;
+}
+
+static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ u32 srccr, srcr, sier;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ bool sync_mode = priv->sync_mode;
+
+ srccr =
+ sync_mode ? __raw_readl(ioaddr + SSI_STCCR) :
+ __raw_readl(ioaddr + SSI_SRCCR);
+ srcr = __raw_readl(ioaddr + SSI_SRCR);
+ sier = __raw_readl(ioaddr + SSI_SIER);
+ srccr &= ~SSI_SRCCR_WL_MASK;
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srccr |= SSI_SRCCR_WL(16);
break;
- case IMX_SSI_RX_DIV_PSR:
- stccr &= ~SSI_STCCR_PSR;
- stccr |= div;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srccr |= SSI_SRCCR_WL(20);
break;
- case IMX_SSI_RX_DIV_PM:
- stccr &= ~0xff;
- stccr |= SSI_STCCR_PM(div);
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srccr |= SSI_SRCCR_WL(24);
break;
- default:
- return -EINVAL;
}
- writel(stccr, ssi->base + SSI_STCCR);
- writel(srccr, ssi->base + SSI_SRCCR);
+ /* enable interrupts */
+ if ((cpu_dai->id & 0x1) == 0)
+ srcr |= SSI_SRCR_RFEN0;
+ else
+ srcr |= SSI_SRCR_RFEN1;
+ sier |= SSI_SIER_RDMAE | SSI_SIER_RIE | SSI_SIER_ROE0_EN;
+
+ __raw_writel(srcr, ioaddr + SSI_SRCR);
+ if (sync_mode)
+ __raw_writel(srccr, ioaddr + SSI_STCCR);
+ else
+ __raw_writel(srccr, ioaddr + SSI_SRCCR);
+ __raw_writel(sier, ioaddr + SSI_SIER);
return 0;
}
@@ -234,530 +467,327 @@ static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
- struct imx_ssi *ssi = cpu_dai->private_data;
- struct imx_pcm_dma_params *dma_data;
- u32 reg, sccr;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ int id;
+
+ id = cpu_dai->id;
/* Tx/Rx config */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- reg = SSI_STCCR;
- dma_data = &ssi->dma_params_tx;
+ /* cant change any parameters when SSI is running */
+ if ((__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_TE))
+ return 0;
+ return imx_ssi_hw_tx_params(substream, params, cpu_dai);
} else {
- reg = SSI_SRCCR;
- dma_data = &ssi->dma_params_rx;
+ /* cant change any parameters when SSI is running */
+ if ((__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_RE))
+ return 0;
+ return imx_ssi_hw_rx_params(substream, params, cpu_dai);
}
+}
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
-
- sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK;
-
- /* DAI data (word) size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- sccr |= SSI_SRCCR_WL(16);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- sccr |= SSI_SRCCR_WL(20);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- sccr |= SSI_SRCCR_WL(24);
- break;
- }
+static int imx_ssi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 scr;
- writel(sccr, ssi->base + reg);
+ /* enable the SSI port, note that no other port config
+ * should happen after SSIEN is set */
+ scr = __raw_readl(ioaddr + SSI_SCR);
+ __raw_writel((scr | SSI_SCR_SSIEN), ioaddr + SSI_SCR);
+ SSI_DUMP();
return 0;
}
static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- struct imx_ssi *ssi = cpu_dai->private_data;
- unsigned int sier_bits, sier;
- unsigned int scr;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ u32 scr;
- scr = readl(ssi->base + SSI_SCR);
- sier = readl(ssi->base + SSI_SIER);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (ssi->flags & IMX_SSI_DMA)
- sier_bits = SSI_SIER_TDMAE;
- else
- sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN;
- } else {
- if (ssi->flags & IMX_SSI_DMA)
- sier_bits = SSI_SIER_RDMAE;
- else
- sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN;
- }
+ scr = __raw_readl(ioaddr + SSI_SCR);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (scr & SSI_SCR_RE) {
+ __raw_writel(0, ioaddr + SSI_SCR);
+ }
scr |= SSI_SCR_TE;
- else
+ } else
scr |= SSI_SCR_RE;
- sier |= sier_bits;
-
- if (++ssi->enabled == 1)
- scr |= SSI_SCR_SSIEN;
-
break;
-
- case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
scr &= ~SSI_SCR_TE;
else
scr &= ~SSI_SCR_RE;
- sier &= ~sier_bits;
-
- if (--ssi->enabled == 0)
- scr &= ~SSI_SCR_SSIEN;
-
break;
default:
return -EINVAL;
}
+ __raw_writel(scr, ioaddr + SSI_SCR);
- if (!(ssi->flags & IMX_SSI_USE_AC97))
- /* rx/tx are always enabled to access ac97 registers */
- writel(scr, ssi->base + SSI_SCR);
-
- writel(sier, ssi->base + SSI_SIER);
-
+ SSI_DUMP();
return 0;
}
-static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
- .hw_params = imx_ssi_hw_params,
- .set_fmt = imx_ssi_set_dai_fmt,
- .set_clkdiv = imx_ssi_set_dai_clkdiv,
- .set_sysclk = imx_ssi_set_dai_sysclk,
- .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
- .trigger = imx_ssi_trigger,
-};
-
-static struct snd_soc_dai imx_ssi_dai = {
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &imx_ssi_pcm_dai_ops,
-};
-
-int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = dma_mmap_coherent(NULL, vma, runtime->dma_area,
- runtime->dma_addr, runtime->dma_bytes);
-
- pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
- return ret;
-}
-
-static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = IMX_SSI_DMABUF_SIZE;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
+ struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data;
+ void __iomem *ioaddr = priv->ioaddr;
+ int id;
- return 0;
-}
+ id = cpu_dai->id;
-static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
-
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
-{
+ /* shutdown SSI if neither Tx or Rx is active */
+ if (cpu_dai->playback.active || cpu_dai->capture.active)
+ return;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &imx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- if (dai->playback.channels_min) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
+ if (clk_get_usecount(priv->ssi_clk) > 1) {
+ clk_disable(priv->ssi_clk);
+ return;
}
- if (dai->capture.channels_min) {
- ret = imx_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
+ __raw_writel(0, ioaddr + SSI_SCR);
-out:
- return ret;
+ clk_disable(priv->ssi_clk);
}
-void imx_pcm_free(struct snd_pcm *pcm)
+#ifdef CONFIG_PM
+static int imx_ssi_suspend(struct snd_soc_dai *dai)
{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-struct snd_soc_platform imx_soc_platform = {
- .name = "imx-audio",
-};
-EXPORT_SYMBOL_GPL(imx_soc_platform);
-
-static struct snd_soc_dai imx_ac97_dai = {
- .name = "AC97",
- .ac97_control = 1,
- .playback = {
- .stream_name = "AC97 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "AC97 Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &imx_ssi_pcm_dai_ops,
-};
-
-static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
-{
- void __iomem *base = imx_ssi->base;
-
- writel(0x0, base + SSI_SCR);
- writel(0x0, base + SSI_STCR);
- writel(0x0, base + SSI_SRCR);
+ if (!dai->active)
+ return 0;
- writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
+ /* do we need to disable any clocks? */
- writel(SSI_SFCSR_RFWM0(8) |
- SSI_SFCSR_TFWM0(8) |
- SSI_SFCSR_RFWM1(8) |
- SSI_SFCSR_TFWM1(8), base + SSI_SFCSR);
-
- writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR);
- writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR);
-
- writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR);
- writel(SSI_SOR_WAIT(3), base + SSI_SOR);
-
- writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN |
- SSI_SCR_TE | SSI_SCR_RE,
- base + SSI_SCR);
-
- writel(SSI_SACNT_DEFAULT, base + SSI_SACNT);
- writel(0xff, base + SSI_SACCDIS);
- writel(0x300, base + SSI_SACCEN);
+ return 0;
}
-static struct imx_ssi *ac97_ssi;
-
-static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
+static int imx_ssi_resume(struct snd_soc_dai *dai)
{
- struct imx_ssi *imx_ssi = ac97_ssi;
- void __iomem *base = imx_ssi->base;
- unsigned int lreg;
- unsigned int lval;
-
- if (reg > 0x7f)
- return;
-
- pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+ if (!dai->active)
+ return 0;
- lreg = reg << 12;
- writel(lreg, base + SSI_SACADD);
+ /* do we need to enable any clocks? */
- lval = val << 4;
- writel(lval , base + SSI_SACDAT);
-
- writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
- udelay(100);
+ return 0;
}
+#else
+#define imx_ssi_suspend NULL
+#define imx_ssi_resume NULL
+#endif
-static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97,
- unsigned short reg)
-{
- struct imx_ssi *imx_ssi = ac97_ssi;
- void __iomem *base = imx_ssi->base;
-
- unsigned short val = -1;
- unsigned int lreg;
-
- lreg = (reg & 0x7f) << 12 ;
- writel(lreg, base + SSI_SACADD);
- writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT);
-
- udelay(100);
-
- val = (readl(base + SSI_SACDAT) >> 4) & 0xffff;
-
- pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+static int fifo_err_counter;
- return val;
+static irqreturn_t imx_ssi_irq(int irq, void *dev_id)
+{
+ struct imx_ssi *priv = (struct imx_ssi *)dev_id;
+ void __iomem *ioaddr = priv->ioaddr;
+ if (fifo_err_counter++ % 1000 == 0)
+ printk(KERN_ERR "%s %s SISR %x SIER %x fifo_errs=%d\n",
+ __func__, priv->pdev->name,
+ __raw_readl(ioaddr + SSI_SISR),
+ __raw_readl(ioaddr + SSI_SIER), fifo_err_counter);
+ __raw_writel((SSI_SIER_TUE0_EN | SSI_SIER_ROE0_EN), ioaddr + SSI_SISR);
+ return IRQ_HANDLED;
}
-static void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
+static int imx_ssi_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
{
- struct imx_ssi *imx_ssi = ac97_ssi;
+ struct imx_ssi *priv = (struct imx_ssi *)dai->private_data;
+
+ if (priv->irq >= 0) {
+ if (request_irq(priv->irq, imx_ssi_irq, IRQF_SHARED,
+ pdev->name, priv)) {
+ printk(KERN_ERR "%s: failure requesting irq for %s\n",
+ __func__, pdev->name);
+ return -EBUSY;
+ }
+ }
- if (imx_ssi->ac97_reset)
- imx_ssi->ac97_reset(ac97);
+ return 0;
}
-static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
+static void imx_ssi_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
{
- struct imx_ssi *imx_ssi = ac97_ssi;
+ struct imx_ssi *priv = (struct imx_ssi *)dai->private_data;
- if (imx_ssi->ac97_warm_reset)
- imx_ssi->ac97_warm_reset(ac97);
+ if (priv->irq >= 0)
+ free_irq(priv->irq, dai);
}
-struct snd_ac97_bus_ops soc_ac97_ops = {
- .read = imx_ssi_ac97_read,
- .write = imx_ssi_ac97_write,
- .reset = imx_ssi_ac97_reset,
- .warm_reset = imx_ssi_ac97_warm_reset
+#define IMX_SSI_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define IMX_SSI_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops imx_ssi_dai_ops = {
+ .startup = imx_ssi_startup,
+ .shutdown = imx_ssi_shutdown,
+ .trigger = imx_ssi_trigger,
+ .prepare = imx_ssi_prepare,
+ .hw_params = imx_ssi_hw_params,
+ .set_sysclk = imx_ssi_set_dai_sysclk,
+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
+ .set_fmt = imx_ssi_set_dai_fmt,
+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
};
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-struct snd_soc_dai imx_ssi_pcm_dai[2];
-EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+struct snd_soc_dai *imx_ssi_dai[MAX_SSI_CHANNELS];
+EXPORT_SYMBOL_GPL(imx_ssi_dai);
-static int imx_ssi_probe(struct platform_device *pdev)
+static int imx_ssi_dev_probe(struct platform_device *pdev)
{
+ int fifo0_channel = pdev->id * 2;
+ struct snd_soc_dai *dai;
+ struct imx_ssi *priv;
+ int fifo, channel;
struct resource *res;
- struct imx_ssi *ssi;
- struct imx_ssi_platform_data *pdata = pdev->dev.platform_data;
- struct snd_soc_platform *platform;
- int ret = 0;
- unsigned int val;
- struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id];
-
- if (dai->id >= ARRAY_SIZE(imx_ssi_pcm_dai))
- return -EINVAL;
+ int ret;
+
+ BUG_ON(fifo0_channel >= MAX_SSI_CHANNELS);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
- ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
- if (!ssi)
+ priv = kzalloc(sizeof(struct imx_ssi), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- if (pdata) {
- ssi->ac97_reset = pdata->ac97_reset;
- ssi->ac97_warm_reset = pdata->ac97_warm_reset;
- ssi->flags = pdata->flags;
- }
+ /* Each SSI block has 2 fifos which share the same
+ private data (struct imx_ssi) */
+ priv->baseaddr = res->start;
+ priv->ioaddr = ioremap(res->start, 0x5C);
+ priv->irq = platform_get_irq(pdev, 0);
+ priv->ssi_clk = clk_get(&pdev->dev, "ssi_clk");
+ priv->pdev = pdev;
+
+ for (fifo = 0; fifo < 2; fifo++) {
+ channel = (pdev->id * 2) + fifo;
+
+ dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
+ if (IS_ERR(dai)) {
+ ret = -ENOMEM;
+ goto DAI_ERR;
+ }
- ssi->irq = platform_get_irq(pdev, 0);
+ dai->name = kasprintf(GFP_KERNEL, "imx-ssi-%d-%d",
+ pdev->id + 1, fifo);
+ if (IS_ERR(dai->name)) {
+ kfree(dai);
+ ret = -ENOMEM;
+ goto DAI_ERR;
+ }
- ssi->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(ssi->clk)) {
- ret = PTR_ERR(ssi->clk);
- dev_err(&pdev->dev, "Cannot get the clock: %d\n",
- ret);
- goto failed_clk;
- }
- clk_enable(ssi->clk);
+ dai->probe = imx_ssi_probe;
+ dai->suspend = imx_ssi_suspend;
+ dai->remove = imx_ssi_remove;
+ dai->resume = imx_ssi_resume;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- goto failed_get_resource;
- }
+ dai->playback.channels_min = 1;
+ dai->playback.channels_max = 2;
+ dai->playback.rates = IMX_SSI_RATES;
+ dai->playback.formats = IMX_SSI_FORMATS;
- if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) {
- dev_err(&pdev->dev, "request_mem_region failed\n");
- ret = -EBUSY;
- goto failed_get_resource;
- }
+ dai->capture.channels_min = 1;
+ dai->capture.channels_max = 2;
+ dai->capture.rates = IMX_SSI_RATES;
+ dai->capture.formats = IMX_SSI_FORMATS;
- ssi->base = ioremap(res->start, resource_size(res));
- if (!ssi->base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- ret = -ENODEV;
- goto failed_ioremap;
- }
+ dai->ops = &imx_ssi_dai_ops;
- if (ssi->flags & IMX_SSI_USE_AC97) {
- if (ac97_ssi) {
- ret = -EBUSY;
- goto failed_ac97;
- }
- ac97_ssi = ssi;
- setup_channel_to_ac97(ssi);
- memcpy(dai, &imx_ac97_dai, sizeof(imx_ac97_dai));
- } else
- memcpy(dai, &imx_ssi_dai, sizeof(imx_ssi_dai));
-
- writel(0x0, ssi->base + SSI_SIER);
-
- ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
- ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
- if (res)
- ssi->dma_params_tx.dma = res->start;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
- if (res)
- ssi->dma_params_rx.dma = res->start;
-
- dai->id = pdev->id;
- dai->dev = &pdev->dev;
- dai->name = kasprintf(GFP_KERNEL, "imx-ssi.%d", pdev->id);
- dai->private_data = ssi;
-
- if ((cpu_is_mx27() || cpu_is_mx21()) &&
- !(ssi->flags & IMX_SSI_USE_AC97) &&
- (ssi->flags & IMX_SSI_DMA)) {
- ssi->flags |= IMX_SSI_DMA;
- platform = imx_ssi_dma_mx2_init(pdev, ssi);
- } else
- platform = imx_ssi_fiq_init(pdev, ssi);
-
- imx_soc_platform.pcm_ops = platform->pcm_ops;
- imx_soc_platform.pcm_new = platform->pcm_new;
- imx_soc_platform.pcm_free = platform->pcm_free;
-
- val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) |
- SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize);
- writel(val, ssi->base + SSI_SFCSR);
-
- ret = snd_soc_register_dai(dai);
- if (ret) {
- dev_err(&pdev->dev, "register DAI failed\n");
- goto failed_register;
- }
+ dai->private_data = priv;
- platform_set_drvdata(pdev, ssi);
+ dai->id = channel;
+ imx_ssi_dai[channel] = dai;
+ ret = snd_soc_register_dai(dai);
+ if (ret < 0) {
+ kfree(dai->name);
+ kfree(dai);
+ goto DAI_ERR;
+ }
+ }
return 0;
-failed_register:
-failed_ac97:
- iounmap(ssi->base);
-failed_ioremap:
- release_mem_region(res->start, resource_size(res));
-failed_get_resource:
- clk_disable(ssi->clk);
- clk_put(ssi->clk);
-failed_clk:
- kfree(ssi);
+DAI_ERR:
+ if (fifo == 1) {
+ dai = imx_ssi_dai[fifo0_channel];
+ snd_soc_unregister_dai(dai);
+ kfree(dai->name);
+ kfree(dai);
+ }
+ clk_put(priv->ssi_clk);
+ iounmap(priv->ioaddr);
+ kfree(priv);
return ret;
}
-static int __devexit imx_ssi_remove(struct platform_device *pdev)
+static int __devexit imx_ssi_dev_remove(struct platform_device *pdev)
{
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct imx_ssi *ssi = platform_get_drvdata(pdev);
- struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id];
-
- snd_soc_unregister_dai(dai);
+ int fifo0_channel = pdev->id * 2;
+ int fifo1_channel = (pdev->id * 2) + 1;
+ struct imx_ssi *priv = imx_ssi_dai[fifo0_channel]->private_data;
- if (ssi->flags & IMX_SSI_USE_AC97)
- ac97_ssi = NULL;
+ snd_soc_unregister_dai(imx_ssi_dai[fifo0_channel]);
+ snd_soc_unregister_dai(imx_ssi_dai[fifo1_channel]);
- if (!(ssi->flags & IMX_SSI_DMA))
- imx_ssi_fiq_exit(pdev, ssi);
+ kfree(imx_ssi_dai[fifo0_channel]->name);
+ kfree(imx_ssi_dai[fifo0_channel]);
- iounmap(ssi->base);
- release_mem_region(res->start, resource_size(res));
- clk_disable(ssi->clk);
- clk_put(ssi->clk);
- kfree(ssi);
+ kfree(imx_ssi_dai[fifo1_channel]->name);
+ kfree(imx_ssi_dai[fifo1_channel]);
+ clk_put(priv->ssi_clk);
+ iounmap(priv->ioaddr);
+ kfree(priv);
return 0;
}
static struct platform_driver imx_ssi_driver = {
- .probe = imx_ssi_probe,
- .remove = __devexit_p(imx_ssi_remove),
-
+ .probe = imx_ssi_dev_probe,
+ .remove = __devexit_p(imx_ssi_dev_remove),
.driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
+ .name = "mxc_ssi",
+ },
};
static int __init imx_ssi_init(void)
{
- int ret;
-
- ret = snd_soc_register_platform(&imx_soc_platform);
- if (ret) {
- pr_err("failed to register soc platform: %d\n", ret);
- return ret;
- }
-
- ret = platform_driver_register(&imx_ssi_driver);
- if (ret) {
- snd_soc_unregister_platform(&imx_soc_platform);
- return ret;
- }
-
- return 0;
+ return platform_driver_register(&imx_ssi_driver);
}
static void __exit imx_ssi_exit(void)
{
platform_driver_unregister(&imx_ssi_driver);
- snd_soc_unregister_platform(&imx_soc_platform);
}
module_init(imx_ssi_init);
module_exit(imx_ssi_exit);
-
-/* Module information */
-MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
-MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
+MODULE_AUTHOR
+ ("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h
index 55f26ebcd8c2..6f0f24a2509d 100644
--- a/sound/soc/imx/imx-ssi.h
+++ b/sound/soc/imx/imx-ssi.h
@@ -7,231 +7,240 @@
#ifndef _IMX_SSI_H
#define _IMX_SSI_H
-#define SSI_STX0 0x00
-#define SSI_STX1 0x04
-#define SSI_SRX0 0x08
-#define SSI_SRX1 0x0c
-
-#define SSI_SCR 0x10
-#define SSI_SCR_CLK_IST (1 << 9)
-#define SSI_SCR_CLK_IST_SHIFT 9
-#define SSI_SCR_TCH_EN (1 << 8)
-#define SSI_SCR_SYS_CLK_EN (1 << 7)
-#define SSI_SCR_I2S_MODE_NORM (0 << 5)
-#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
-#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
-#define SSI_I2S_MODE_MASK (3 << 5)
-#define SSI_SCR_SYN (1 << 4)
-#define SSI_SCR_NET (1 << 3)
-#define SSI_SCR_RE (1 << 2)
-#define SSI_SCR_TE (1 << 1)
-#define SSI_SCR_SSIEN (1 << 0)
-
-#define SSI_SISR 0x14
-#define SSI_SISR_MASK ((1 << 19) - 1)
-#define SSI_SISR_CMDAU (1 << 18)
-#define SSI_SISR_CMDDU (1 << 17)
-#define SSI_SISR_RXT (1 << 16)
-#define SSI_SISR_RDR1 (1 << 15)
-#define SSI_SISR_RDR0 (1 << 14)
-#define SSI_SISR_TDE1 (1 << 13)
-#define SSI_SISR_TDE0 (1 << 12)
-#define SSI_SISR_ROE1 (1 << 11)
-#define SSI_SISR_ROE0 (1 << 10)
-#define SSI_SISR_TUE1 (1 << 9)
-#define SSI_SISR_TUE0 (1 << 8)
-#define SSI_SISR_TFS (1 << 7)
-#define SSI_SISR_RFS (1 << 6)
-#define SSI_SISR_TLS (1 << 5)
-#define SSI_SISR_RLS (1 << 4)
-#define SSI_SISR_RFF1 (1 << 3)
-#define SSI_SISR_RFF0 (1 << 2)
-#define SSI_SISR_TFE1 (1 << 1)
-#define SSI_SISR_TFE0 (1 << 0)
-
-#define SSI_SIER 0x18
-#define SSI_SIER_RDMAE (1 << 22)
-#define SSI_SIER_RIE (1 << 21)
-#define SSI_SIER_TDMAE (1 << 20)
-#define SSI_SIER_TIE (1 << 19)
-#define SSI_SIER_CMDAU_EN (1 << 18)
-#define SSI_SIER_CMDDU_EN (1 << 17)
-#define SSI_SIER_RXT_EN (1 << 16)
-#define SSI_SIER_RDR1_EN (1 << 15)
-#define SSI_SIER_RDR0_EN (1 << 14)
-#define SSI_SIER_TDE1_EN (1 << 13)
-#define SSI_SIER_TDE0_EN (1 << 12)
-#define SSI_SIER_ROE1_EN (1 << 11)
-#define SSI_SIER_ROE0_EN (1 << 10)
-#define SSI_SIER_TUE1_EN (1 << 9)
-#define SSI_SIER_TUE0_EN (1 << 8)
-#define SSI_SIER_TFS_EN (1 << 7)
-#define SSI_SIER_RFS_EN (1 << 6)
-#define SSI_SIER_TLS_EN (1 << 5)
-#define SSI_SIER_RLS_EN (1 << 4)
-#define SSI_SIER_RFF1_EN (1 << 3)
-#define SSI_SIER_RFF0_EN (1 << 2)
-#define SSI_SIER_TFE1_EN (1 << 1)
-#define SSI_SIER_TFE0_EN (1 << 0)
-
-#define SSI_STCR 0x1c
-#define SSI_STCR_TXBIT0 (1 << 9)
-#define SSI_STCR_TFEN1 (1 << 8)
-#define SSI_STCR_TFEN0 (1 << 7)
-#define SSI_FIFO_ENABLE_0_SHIFT 7
-#define SSI_STCR_TFDIR (1 << 6)
-#define SSI_STCR_TXDIR (1 << 5)
-#define SSI_STCR_TSHFD (1 << 4)
-#define SSI_STCR_TSCKP (1 << 3)
-#define SSI_STCR_TFSI (1 << 2)
-#define SSI_STCR_TFSL (1 << 1)
-#define SSI_STCR_TEFS (1 << 0)
-
-#define SSI_SRCR 0x20
-#define SSI_SRCR_RXBIT0 (1 << 9)
-#define SSI_SRCR_RFEN1 (1 << 8)
-#define SSI_SRCR_RFEN0 (1 << 7)
-#define SSI_FIFO_ENABLE_0_SHIFT 7
-#define SSI_SRCR_RFDIR (1 << 6)
-#define SSI_SRCR_RXDIR (1 << 5)
-#define SSI_SRCR_RSHFD (1 << 4)
-#define SSI_SRCR_RSCKP (1 << 3)
-#define SSI_SRCR_RFSI (1 << 2)
-#define SSI_SRCR_RFSL (1 << 1)
-#define SSI_SRCR_REFS (1 << 0)
-
-#define SSI_SRCCR 0x28
-#define SSI_SRCCR_DIV2 (1 << 18)
-#define SSI_SRCCR_PSR (1 << 17)
-#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
-#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
-#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
-#define SSI_SRCCR_WL_MASK (0xf << 13)
-#define SSI_SRCCR_DC_MASK (0x1f << 8)
-#define SSI_SRCCR_PM_MASK (0xff << 0)
-
-#define SSI_STCCR 0x24
-#define SSI_STCCR_DIV2 (1 << 18)
-#define SSI_STCCR_PSR (1 << 17)
-#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
-#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
-#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
-#define SSI_STCCR_WL_MASK (0xf << 13)
-#define SSI_STCCR_DC_MASK (0x1f << 8)
-#define SSI_STCCR_PM_MASK (0xff << 0)
-
-#define SSI_SFCSR 0x2c
-#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
-#define SSI_RX_FIFO_1_COUNT_SHIFT 28
-#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
-#define SSI_TX_FIFO_1_COUNT_SHIFT 24
-#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
-#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
-#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
-#define SSI_RX_FIFO_0_COUNT_SHIFT 12
-#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
-#define SSI_TX_FIFO_0_COUNT_SHIFT 8
-#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
-#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
-#define SSI_SFCSR_RFWM0_MASK (0xf << 4)
-#define SSI_SFCSR_TFWM0_MASK (0xf << 0)
-
-#define SSI_STR 0x30
-#define SSI_STR_TEST (1 << 15)
-#define SSI_STR_RCK2TCK (1 << 14)
-#define SSI_STR_RFS2TFS (1 << 13)
-#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
-#define SSI_STR_TXD2RXD (1 << 7)
-#define SSI_STR_TCK2RCK (1 << 6)
-#define SSI_STR_TFS2RFS (1 << 5)
-#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
-
-#define SSI_SOR 0x34
-#define SSI_SOR_CLKOFF (1 << 6)
-#define SSI_SOR_RX_CLR (1 << 5)
-#define SSI_SOR_TX_CLR (1 << 4)
-#define SSI_SOR_INIT (1 << 3)
-#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
-#define SSI_SOR_WAIT_MASK (0x3 << 1)
-#define SSI_SOR_SYNRST (1 << 0)
-
-#define SSI_SACNT 0x38
-#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
-#define SSI_SACNT_WR (1 << 4)
-#define SSI_SACNT_RD (1 << 3)
-#define SSI_SACNT_TIF (1 << 2)
-#define SSI_SACNT_FV (1 << 1)
-#define SSI_SACNT_AC97EN (1 << 0)
-
-#define SSI_SACADD 0x3c
-#define SSI_SACDAT 0x40
-#define SSI_SATAG 0x44
-#define SSI_STMSK 0x48
-#define SSI_SRMSK 0x4c
-#define SSI_SACCST 0x50
-#define SSI_SACCEN 0x54
-#define SSI_SACCDIS 0x58
+#include <mach/hardware.h>
+
+/* SSI regs definition */
+#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR)
+#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR)
+
+#define SSI1_STX0 ((SSI1_IO_BASE_ADDR) + 0x00)
+#define SSI1_STX1 ((SSI1_IO_BASE_ADDR) + 0x04)
+#define SSI1_SRX0 ((SSI1_IO_BASE_ADDR) + 0x08)
+#define SSI1_SRX1 ((SSI1_IO_BASE_ADDR) + 0x0c)
+#define SSI1_SCR ((SSI1_IO_BASE_ADDR) + 0x10)
+#define SSI1_SISR ((SSI1_IO_BASE_ADDR) + 0x14)
+#define SSI1_SIER ((SSI1_IO_BASE_ADDR) + 0x18)
+#define SSI1_STCR ((SSI1_IO_BASE_ADDR) + 0x1c)
+#define SSI1_SRCR ((SSI1_IO_BASE_ADDR) + 0x20)
+#define SSI1_STCCR ((SSI1_IO_BASE_ADDR) + 0x24)
+#define SSI1_SRCCR ((SSI1_IO_BASE_ADDR) + 0x28)
+#define SSI1_SFCSR ((SSI1_IO_BASE_ADDR) + 0x2c)
+#define SSI1_STR ((SSI1_IO_BASE_ADDR) + 0x30)
+#define SSI1_SOR ((SSI1_IO_BASE_ADDR) + 0x34)
+#define SSI1_SACNT ((SSI1_IO_BASE_ADDR) + 0x38)
+#define SSI1_SACADD ((SSI1_IO_BASE_ADDR) + 0x3c)
+#define SSI1_SACDAT ((SSI1_IO_BASE_ADDR) + 0x40)
+#define SSI1_SATAG ((SSI1_IO_BASE_ADDR) + 0x44)
+#define SSI1_STMSK ((SSI1_IO_BASE_ADDR) + 0x48)
+#define SSI1_SRMSK ((SSI1_IO_BASE_ADDR) + 0x4c)
+#define SSI1_SACCST ((SSI1_IO_BASE_ADDR) + 0x50)
+#define SSI1_SACCEN ((SSI1_IO_BASE_ADDR) + 0x54)
+#define SSI1_SACCDIS ((SSI1_IO_BASE_ADDR) + 0x58)
+
+#define SSI2_STX0 ((SSI2_IO_BASE_ADDR) + 0x00)
+#define SSI2_STX1 ((SSI2_IO_BASE_ADDR) + 0x04)
+#define SSI2_SRX0 ((SSI2_IO_BASE_ADDR) + 0x08)
+#define SSI2_SRX1 ((SSI2_IO_BASE_ADDR) + 0x0c)
+#define SSI2_SCR ((SSI2_IO_BASE_ADDR) + 0x10)
+#define SSI2_SISR ((SSI2_IO_BASE_ADDR) + 0x14)
+#define SSI2_SIER ((SSI2_IO_BASE_ADDR) + 0x18)
+#define SSI2_STCR ((SSI2_IO_BASE_ADDR) + 0x1c)
+#define SSI2_SRCR ((SSI2_IO_BASE_ADDR) + 0x20)
+#define SSI2_STCCR ((SSI2_IO_BASE_ADDR) + 0x24)
+#define SSI2_SRCCR ((SSI2_IO_BASE_ADDR) + 0x28)
+#define SSI2_SFCSR ((SSI2_IO_BASE_ADDR) + 0x2c)
+#define SSI2_STR ((SSI2_IO_BASE_ADDR) + 0x30)
+#define SSI2_SOR ((SSI2_IO_BASE_ADDR) + 0x34)
+#define SSI2_SACNT ((SSI2_IO_BASE_ADDR) + 0x38)
+#define SSI2_SACADD ((SSI2_IO_BASE_ADDR) + 0x3c)
+#define SSI2_SACDAT ((SSI2_IO_BASE_ADDR) + 0x40)
+#define SSI2_SATAG ((SSI2_IO_BASE_ADDR) + 0x44)
+#define SSI2_STMSK ((SSI2_IO_BASE_ADDR) + 0x48)
+#define SSI2_SRMSK ((SSI2_IO_BASE_ADDR) + 0x4c)
+#define SSI2_SACCST ((SSI2_IO_BASE_ADDR) + 0x50)
+#define SSI2_SACCEN ((SSI2_IO_BASE_ADDR) + 0x54)
+#define SSI2_SACCDIS ((SSI2_IO_BASE_ADDR) + 0x58)
+
+#define SSI_SCR_CLK_IST (1 << 9)
+#define SSI_SCR_TCH_EN (1 << 8)
+#define SSI_SCR_SYS_CLK_EN (1 << 7)
+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
+#define SSI_SCR_SYN (1 << 4)
+#define SSI_SCR_NET (1 << 3)
+#define SSI_SCR_RE (1 << 2)
+#define SSI_SCR_TE (1 << 1)
+#define SSI_SCR_SSIEN (1 << 0)
+#define SSI_SCR_I2S_MODE_MASK (3 << 5)
+
+#define SSI_SISR_CMDAU (1 << 18)
+#define SSI_SISR_CMDDU (1 << 17)
+#define SSI_SISR_RXT (1 << 16)
+#define SSI_SISR_RDR1 (1 << 15)
+#define SSI_SISR_RDR0 (1 << 14)
+#define SSI_SISR_TDE1 (1 << 13)
+#define SSI_SISR_TDE0 (1 << 12)
+#define SSI_SISR_ROE1 (1 << 11)
+#define SSI_SISR_ROE0 (1 << 10)
+#define SSI_SISR_TUE1 (1 << 9)
+#define SSI_SISR_TUE0 (1 << 8)
+#define SSI_SISR_TFS (1 << 7)
+#define SSI_SISR_RFS (1 << 6)
+#define SSI_SISR_TLS (1 << 5)
+#define SSI_SISR_RLS (1 << 4)
+#define SSI_SISR_RFF1 (1 << 3)
+#define SSI_SISR_RFF0 (1 << 2)
+#define SSI_SISR_TFE1 (1 << 1)
+#define SSI_SISR_TFE0 (1 << 0)
+
+#define SSI_SIER_RDMAE (1 << 22)
+#define SSI_SIER_RIE (1 << 21)
+#define SSI_SIER_TDMAE (1 << 20)
+#define SSI_SIER_TIE (1 << 19)
+#define SSI_SIER_CMDAU_EN (1 << 18)
+#define SSI_SIER_CMDDU_EN (1 << 17)
+#define SSI_SIER_RXT_EN (1 << 16)
+#define SSI_SIER_RDR1_EN (1 << 15)
+#define SSI_SIER_RDR0_EN (1 << 14)
+#define SSI_SIER_TDE1_EN (1 << 13)
+#define SSI_SIER_TDE0_EN (1 << 12)
+#define SSI_SIER_ROE1_EN (1 << 11)
+#define SSI_SIER_ROE0_EN (1 << 10)
+#define SSI_SIER_TUE1_EN (1 << 9)
+#define SSI_SIER_TUE0_EN (1 << 8)
+#define SSI_SIER_TFS_EN (1 << 7)
+#define SSI_SIER_RFS_EN (1 << 6)
+#define SSI_SIER_TLS_EN (1 << 5)
+#define SSI_SIER_RLS_EN (1 << 4)
+#define SSI_SIER_RFF1_EN (1 << 3)
+#define SSI_SIER_RFF0_EN (1 << 2)
+#define SSI_SIER_TFE1_EN (1 << 1)
+#define SSI_SIER_TFE0_EN (1 << 0)
+
+#define SSI_STCR_TXBIT0 (1 << 9)
+#define SSI_STCR_TFEN1 (1 << 8)
+#define SSI_STCR_TFEN0 (1 << 7)
+#define SSI_STCR_TFDIR (1 << 6)
+#define SSI_STCR_TXDIR (1 << 5)
+#define SSI_STCR_TSHFD (1 << 4)
+#define SSI_STCR_TSCKP (1 << 3)
+#define SSI_STCR_TFSI (1 << 2)
+#define SSI_STCR_TFSL (1 << 1)
+#define SSI_STCR_TEFS (1 << 0)
+
+#define SSI_SRCR_RXBIT0 (1 << 9)
+#define SSI_SRCR_RFEN1 (1 << 8)
+#define SSI_SRCR_RFEN0 (1 << 7)
+#define SSI_SRCR_RFDIR (1 << 6)
+#define SSI_SRCR_RXDIR (1 << 5)
+#define SSI_SRCR_RSHFD (1 << 4)
+#define SSI_SRCR_RSCKP (1 << 3)
+#define SSI_SRCR_RFSI (1 << 2)
+#define SSI_SRCR_RFSL (1 << 1)
+#define SSI_SRCR_REFS (1 << 0)
+
+#define SSI_STCCR_DIV2 (1 << 18)
+#define SSI_STCCR_PSR (1 << 15)
+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK (0xf << 13)
+#define SSI_STCCR_DC_MASK (0x1f << 8)
+#define SSI_STCCR_PM_MASK (0xff << 0)
+
+#define SSI_SRCCR_DIV2 (1 << 18)
+#define SSI_SRCCR_PSR (1 << 15)
+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK (0xf << 13)
+#define SSI_SRCCR_DC_MASK (0x1f << 8)
+#define SSI_SRCCR_PM_MASK (0xff << 0)
+
+
+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
+
+#define SSI_STR_TEST (1 << 15)
+#define SSI_STR_RCK2TCK (1 << 14)
+#define SSI_STR_RFS2TFS (1 << 13)
+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD (1 << 7)
+#define SSI_STR_TCK2RCK (1 << 6)
+#define SSI_STR_TFS2RFS (1 << 5)
+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
+
+#define SSI_SOR_CLKOFF (1 << 6)
+#define SSI_SOR_RX_CLR (1 << 5)
+#define SSI_SOR_TX_CLR (1 << 4)
+#define SSI_SOR_INIT (1 << 3)
+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
+#define SSI_SOR_SYNRST (1 << 0)
+
+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR (1 << 4)
+#define SSI_SACNT_RD (1 << 3)
+#define SSI_SACNT_TIF (1 << 2)
+#define SSI_SACNT_FV (1 << 1)
+#define SSI_SACNT_AC97EN (1 << 0)
+
+/* SDMA & SSI watermarks for FIFO's */
+#define SDMA_TXFIFO_WATERMARK 0x4
+#define SDMA_RXFIFO_WATERMARK 0x6
+#define SSI_TXFIFO_WATERMARK 0x4
+#define SSI_RXFIFO_WATERMARK 0x6
+
+/* Maximum number of ssi channels (counting two channels per block) */
+#define MAX_SSI_CHANNELS 12
+
+/* i.MX DAI SSP ID's */
+#define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */
+#define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */
+#define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */
+#define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */
+#define IMX_DAI_SSI4 4 /* SSI3 FIFO 0 */
+#define IMX_DAI_SSI5 5 /* SSI3 FIFO 1 */
/* SSI clock sources */
-#define IMX_SSP_SYS_CLK 0
+#define IMX_SSP_SYS_CLK 0
/* SSI audio dividers */
-#define IMX_SSI_TX_DIV_2 0
-#define IMX_SSI_TX_DIV_PSR 1
-#define IMX_SSI_TX_DIV_PM 2
-#define IMX_SSI_RX_DIV_2 3
-#define IMX_SSI_RX_DIV_PSR 4
-#define IMX_SSI_RX_DIV_PM 5
-
-extern struct snd_soc_dai imx_ssi_pcm_dai[2];
-extern struct snd_soc_platform imx_soc_platform;
-
-#define DRV_NAME "imx-ssi"
-
-struct imx_pcm_dma_params {
- int dma;
- unsigned long dma_addr;
- int burstsize;
-};
-
-struct imx_ssi {
- struct platform_device *ac97_dev;
+#define IMX_SSI_TX_DIV_2 0
+#define IMX_SSI_TX_DIV_PSR 1
+#define IMX_SSI_TX_DIV_PM 2
+#define IMX_SSI_RX_DIV_2 3
+#define IMX_SSI_RX_DIV_PSR 4
+#define IMX_SSI_RX_DIV_PM 5
- struct snd_soc_device imx_ac97;
- struct clk *clk;
- void __iomem *base;
- int irq;
- int fiq_enable;
- unsigned int offset;
-
- unsigned int flags;
- void (*ac97_reset) (struct snd_ac97 *ac97);
- void (*ac97_warm_reset)(struct snd_ac97 *ac97);
+/* SSI Div 2 */
+#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2)
+#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
- struct imx_pcm_dma_params dma_params_rx;
- struct imx_pcm_dma_params dma_params_tx;
+#define IMX_DAI_AC97_1 0
+#define IMX_DAI_AC97_2 1
- int enabled;
+/* private info */
+struct imx_ssi {
+ bool network_mode;
+ bool sync_mode;
+ unsigned int ac97_tx_slots;
+ unsigned int ac97_rx_slots;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ int irq;
+ struct platform_device *pdev;
+ struct clk *ssi_clk;
};
-struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev,
- struct imx_ssi *ssi);
-void imx_ssi_fiq_exit(struct platform_device *pdev, struct imx_ssi *ssi);
-struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
- struct imx_ssi *ssi);
-
-int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
- struct snd_pcm *pcm);
-void imx_pcm_free(struct snd_pcm *pcm);
-
-/*
- * Do not change this as the FIQ handler depends on this size
- */
-#define IMX_SSI_DMABUF_SIZE (64 * 1024)
-
-#define DMA_RXFIFO_BURST 0x4
-#define DMA_TXFIFO_BURST 0x6
+extern struct snd_soc_dai *imx_ssi_dai[];
+extern struct snd_soc_dai imx_ac97_dai[];
-#endif /* _IMX_SSI_H */
+#endif