summaryrefslogtreecommitdiff
path: root/sound/soc/imx
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2010-03-08 23:21:04 +0300
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-03-08 23:21:04 +0300
commit988addf82e4c03739375279de73929580a2d4a6a (patch)
tree989ae1cd4e264bbad80c65f04480486246e7b9f3 /sound/soc/imx
parent004c1c7096659d352b83047a7593e91d8a30e3c5 (diff)
parent25cf84cf377c0aae5dbcf937ea89bc7893db5176 (diff)
downloadlinux-988addf82e4c03739375279de73929580a2d4a6a.tar.xz
Merge branch 'origin' into devel-stable
Conflicts: arch/arm/mach-mx2/devices.c arch/arm/mach-mx2/devices.h sound/soc/pxa/pxa-ssp.c
Diffstat (limited to 'sound/soc/imx')
-rw-r--r--sound/soc/imx/Kconfig20
-rw-r--r--sound/soc/imx/Makefile14
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c313
-rw-r--r--sound/soc/imx/imx-pcm-fiq.c297
-rw-r--r--sound/soc/imx/imx-ssi.c758
-rw-r--r--sound/soc/imx/imx-ssi.h237
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.c488
-rw-r--r--sound/soc/imx/mx1_mx2-pcm.h26
-rw-r--r--sound/soc/imx/mx27vis_wm8974.c318
-rw-r--r--sound/soc/imx/mxc-ssi.c860
-rw-r--r--sound/soc/imx/mxc-ssi.h238
-rw-r--r--sound/soc/imx/phycore-ac97.c90
12 files changed, 1709 insertions, 1950 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index a700562e8692..c7d0fd9b7de8 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -1,21 +1,13 @@
-config SND_MX1_MX2_SOC
- tristate "SoC Audio for Freecale i.MX1x i.MX2x CPUs"
- depends on ARCH_MX2 || ARCH_MX1
+config SND_IMX_SOC
+ tristate "SoC Audio for Freescale i.MX CPUs"
+ depends on ARCH_MXC && BROKEN
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 MX1 or MX2 SSI interface.
+ the i.MX SSI interface.
config SND_MXC_SOC_SSI
tristate
-config SND_SOC_MX27VIS_WM8974
- tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board"
- depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10
- select SND_MXC_SOC_SSI
- select SND_SOC_WM8974
- help
- Say Y if you want to add support for SoC audio on Visstrim SM10
- board with WM8974.
-
-
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index c2ffd2c8df5a..9f8bb92ddfcc 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -1,10 +1,12 @@
# i.MX Platform Support
-snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o
-snd-soc-mxc-ssi-objs := mxc-ssi.o
+snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o
-obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o
-obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.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-mx27vis-wm8974-objs := mx27vis_wm8974.o
-obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o
+snd-soc-phycore-ac97-objs := phycore-ac97.o
+obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c
new file mode 100644
index 000000000000..19452e44afdc
--- /dev/null
+++ b/sound/soc/imx/imx-pcm-dma-mx2.c
@@ -0,0 +1,313 @@
+/*
+ * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ * 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.
+ */
+#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/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/dma-mx1-mx2.h>
+
+#include "imx-ssi.h"
+
+struct imx_pcm_runtime_data {
+ int sg_count;
+ struct scatterlist *sg_list;
+ int period;
+ int periods;
+ unsigned long dma_addr;
+ int dma;
+ struct snd_pcm_substream *substream;
+ unsigned long offset;
+ unsigned long size;
+ unsigned long period_cnt;
+ void *buf;
+ int period_time;
+};
+
+/* Called by the DMA framework when a period has elapsed */
+static void imx_ssi_dma_progression(int channel, void *data,
+ struct scatterlist *sg)
+{
+ struct snd_pcm_substream *substream = data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ if (!sg)
+ return;
+
+ runtime = iprtd->substream->runtime;
+
+ iprtd->offset = sg->dma_address - runtime->dma_addr;
+
+ snd_pcm_period_elapsed(iprtd->substream);
+}
+
+static void imx_ssi_dma_callback(int channel, void *data)
+{
+ pr_err("%s shouldn't be called\n", __func__);
+}
+
+static void snd_imx_dma_err_callback(int channel, void *data, int err)
+{
+ pr_err("DMA error callback called\n");
+
+ pr_err("DMA timeout on channel %d -%s%s%s%s\n",
+ channel,
+ err & IMX_DMA_ERR_BURST ? " burst" : "",
+ err & IMX_DMA_ERR_REQUEST ? " request" : "",
+ err & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
+ err & IMX_DMA_ERR_BUFFER ? " buffer" : "");
+}
+
+static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ int ret;
+
+ iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH);
+ if (iprtd->dma < 0) {
+ pr_err("Failed to claim the audio DMA\n");
+ return -ENODEV;
+ }
+
+ ret = imx_dma_setup_handlers(iprtd->dma,
+ imx_ssi_dma_callback,
+ snd_imx_dma_err_callback, substream);
+ if (ret)
+ goto out;
+
+ ret = imx_dma_setup_progression_handler(iprtd->dma,
+ imx_ssi_dma_progression);
+ if (ret) {
+ pr_err("Failed to setup the DMA handler\n");
+ goto out;
+ }
+
+ ret = imx_dma_config_channel(iprtd->dma,
+ IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+ dma_params->dma, 1);
+ if (ret < 0) {
+ pr_err("Cannot configure DMA channel: %d\n", ret);
+ goto out;
+ }
+
+ imx_dma_config_burstlen(iprtd->dma, dma_params->burstsize * 2);
+
+ return 0;
+out:
+ imx_dma_free(iprtd->dma);
+ return ret;
+}
+
+static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ int i;
+ unsigned long dma_addr;
+
+ imx_ssi_dma_alloc(substream);
+
+ iprtd->size = params_buffer_bytes(params);
+ iprtd->periods = params_periods(params);
+ iprtd->period = params_period_bytes(params);
+ iprtd->offset = 0;
+ iprtd->period_time = HZ / (params_rate(params) /
+ params_period_size(params));
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ if (iprtd->sg_count != iprtd->periods) {
+ kfree(iprtd->sg_list);
+
+ iprtd->sg_list = kcalloc(iprtd->periods + 1,
+ sizeof(struct scatterlist), GFP_KERNEL);
+ if (!iprtd->sg_list)
+ return -ENOMEM;
+ iprtd->sg_count = iprtd->periods + 1;
+ }
+
+ sg_init_table(iprtd->sg_list, iprtd->sg_count);
+ dma_addr = runtime->dma_addr;
+
+ for (i = 0; i < iprtd->periods; i++) {
+ iprtd->sg_list[i].page_link = 0;
+ iprtd->sg_list[i].offset = 0;
+ iprtd->sg_list[i].dma_address = dma_addr;
+ iprtd->sg_list[i].length = iprtd->period;
+ dma_addr += iprtd->period;
+ }
+
+ /* close the loop */
+ iprtd->sg_list[iprtd->sg_count - 1].offset = 0;
+ iprtd->sg_list[iprtd->sg_count - 1].length = 0;
+ iprtd->sg_list[iprtd->sg_count - 1].page_link =
+ ((unsigned long) iprtd->sg_list | 0x01) & ~0x02;
+ return 0;
+}
+
+static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ if (iprtd->dma >= 0) {
+ imx_dma_free(iprtd->dma);
+ iprtd->dma = -EINVAL;
+ }
+
+ kfree(iprtd->sg_list);
+ iprtd->sg_list = NULL;
+
+ return 0;
+}
+
+static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ int err;
+
+ iprtd->substream = substream;
+ iprtd->buf = (unsigned int *)substream->dma_buffer.area;
+ iprtd->period_cnt = 0;
+
+ pr_debug("%s: buf: %p period: %d periods: %d\n",
+ __func__, iprtd->buf, iprtd->period, iprtd->periods);
+
+ err = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count,
+ IMX_DMA_LENGTH_LOOP, dma_params->dma_addr,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ DMA_MODE_WRITE : DMA_MODE_READ);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ imx_dma_enable(iprtd->dma);
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ imx_dma_disable(iprtd->dma);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static struct snd_pcm_hardware snd_imx_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,
+ .rate_min = 8000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 16 * 1024,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static int snd_imx_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd;
+ int ret;
+
+ iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+ runtime->private_data = iprtd;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+ return 0;
+}
+
+static struct snd_pcm_ops imx_pcm_ops = {
+ .open = snd_imx_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_imx_pcm_hw_params,
+ .hw_free = snd_imx_pcm_hw_free,
+ .prepare = snd_imx_pcm_prepare,
+ .trigger = snd_imx_pcm_trigger,
+ .pointer = snd_imx_pcm_pointer,
+ .mmap = snd_imx_pcm_mmap,
+};
+
+static struct snd_soc_platform imx_soc_platform_dma = {
+ .name = "imx-audio",
+ .pcm_ops = &imx_pcm_ops,
+ .pcm_new = imx_pcm_new,
+ .pcm_free = imx_pcm_free,
+};
+
+struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
+ struct imx_ssi *ssi)
+{
+ ssi->dma_params_tx.burstsize = DMA_TXFIFO_BURST;
+ ssi->dma_params_rx.burstsize = DMA_RXFIFO_BURST;
+
+ return &imx_soc_platform_dma;
+}
+
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
new file mode 100644
index 000000000000..d9cb9849b033
--- /dev/null
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -0,0 +1,297 @@
+/*
+ * imx-pcm-fiq.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ * 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.
+ */
+#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/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/fiq.h>
+
+#include <mach/ssi.h>
+
+#include "imx-ssi.h"
+
+struct imx_pcm_runtime_data {
+ int period;
+ int periods;
+ unsigned long offset;
+ unsigned long last_offset;
+ unsigned long size;
+ struct timer_list timer;
+ int poll_time;
+};
+
+static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd)
+{
+ iprtd->timer.expires = jiffies + iprtd->poll_time;
+}
+
+static void imx_ssi_timer_callback(unsigned long data)
+{
+ struct snd_pcm_substream *substream = (void *)data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ struct pt_regs regs;
+ unsigned long delta;
+
+ get_fiq_regs(&regs);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ iprtd->offset = regs.ARM_r8 & 0xffff;
+ else
+ iprtd->offset = regs.ARM_r9 & 0xffff;
+
+ /* How much data have we transferred since the last period report? */
+ if (iprtd->offset >= iprtd->last_offset)
+ delta = iprtd->offset - iprtd->last_offset;
+ else
+ delta = runtime->buffer_size + iprtd->offset
+ - iprtd->last_offset;
+
+ /* If we've transferred at least a period then report it and
+ * reset our poll time */
+ if (delta >= runtime->period_size) {
+ snd_pcm_period_elapsed(substream);
+ iprtd->last_offset = iprtd->offset;
+
+ imx_ssi_set_next_poll(iprtd);
+ }
+
+ /* Restart the timer; if we didn't report we'll run on the next tick */
+ add_timer(&iprtd->timer);
+
+}
+
+static struct fiq_handler fh = {
+ .name = DRV_NAME,
+};
+
+static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ iprtd->size = params_buffer_bytes(params);
+ iprtd->periods = params_periods(params);
+ iprtd->period = params_period_bytes(params) ;
+ iprtd->offset = 0;
+ iprtd->last_offset = 0;
+ iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params));
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ struct pt_regs regs;
+
+ get_fiq_regs(&regs);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16;
+ else
+ regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16;
+
+ set_fiq_regs(&regs);
+
+ return 0;
+}
+
+static int fiq_enable;
+static int imx_pcm_fiq;
+
+static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ imx_ssi_set_next_poll(iprtd);
+ add_timer(&iprtd->timer);
+ if (++fiq_enable == 1)
+ enable_fiq(imx_pcm_fiq);
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ del_timer(&iprtd->timer);
+ if (--fiq_enable == 0)
+ disable_fiq(imx_pcm_fiq);
+
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static struct snd_pcm_hardware snd_imx_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,
+ .rate_min = 8000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 16 * 1024,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static int snd_imx_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd;
+ int ret;
+
+ iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+ runtime->private_data = iprtd;
+
+ init_timer(&iprtd->timer);
+ iprtd->timer.data = (unsigned long)substream;
+ iprtd->timer.function = imx_ssi_timer_callback;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+ return 0;
+}
+
+static int snd_imx_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+ del_timer_sync(&iprtd->timer);
+ kfree(iprtd);
+
+ return 0;
+}
+
+static struct snd_pcm_ops imx_pcm_ops = {
+ .open = snd_imx_open,
+ .close = snd_imx_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_imx_pcm_hw_params,
+ .prepare = snd_imx_pcm_prepare,
+ .trigger = snd_imx_pcm_trigger,
+ .pointer = snd_imx_pcm_pointer,
+ .mmap = snd_imx_pcm_mmap,
+};
+
+static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret;
+
+ ret = imx_pcm_new(card, dai, pcm);
+ if (ret)
+ return ret;
+
+ if (dai->playback.channels_min) {
+ struct snd_pcm_substream *substream =
+ pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+ imx_ssi_fiq_tx_buffer = (unsigned long)buf->area;
+ }
+
+ if (dai->capture.channels_min) {
+ struct snd_pcm_substream *substream =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+ imx_ssi_fiq_rx_buffer = (unsigned long)buf->area;
+ }
+
+ set_fiq_handler(&imx_ssi_fiq_start,
+ &imx_ssi_fiq_end - &imx_ssi_fiq_start);
+
+ return 0;
+}
+
+static struct snd_soc_platform imx_soc_platform_fiq = {
+ .pcm_ops = &imx_pcm_ops,
+ .pcm_new = imx_pcm_fiq_new,
+ .pcm_free = imx_pcm_free,
+};
+
+struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev,
+ struct imx_ssi *ssi)
+{
+ int ret = 0;
+
+ ret = claim_fiq(&fh);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim fiq: %d", ret);
+ return ERR_PTR(ret);
+ }
+
+ mxc_set_irq_fiq(ssi->irq, 1);
+
+ imx_pcm_fiq = ssi->irq;
+
+ imx_ssi_fiq_base = (unsigned long)ssi->base;
+
+ ssi->dma_params_tx.burstsize = 4;
+ ssi->dma_params_rx.burstsize = 6;
+
+ return &imx_soc_platform_fiq;
+}
+
+void imx_ssi_fiq_exit(struct platform_device *pdev,
+ struct imx_ssi *ssi)
+{
+ mxc_set_irq_fiq(ssi->irq, 0);
+ release_fiq(&fh);
+}
+
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
new file mode 100644
index 000000000000..56f46a75d297
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.c
@@ -0,0 +1,758 @@
+/*
+ * imx-ssi.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ * 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.
+ *
+ *
+ * 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/platform_device.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/hardware.h>
+
+#include "imx-ssi.h"
+
+#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
+
+/*
+ * 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)
+{
+ struct imx_ssi *ssi = cpu_dai->private_data;
+ u32 sccr;
+
+ sccr = readl(ssi->base + SSI_STCCR);
+ sccr &= ~SSI_STCCR_DC_MASK;
+ sccr |= SSI_STCCR_DC(slots - 1);
+ writel(sccr, ssi->base + 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);
+
+ writel(tx_mask, ssi->base + SSI_STMSK);
+ writel(rx_mask, ssi->base + SSI_SRMSK);
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+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;
+
+ scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+ /* 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;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ strcr |= SSI_STCR_TXBIT0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ strcr |= SSI_STCR_TFSL;
+ 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;
+ 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;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ strcr &= ~SSI_STCR_TFSI;
+ strcr |= SSI_STCR_TSCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ /* Master mode not implemented, needs handling of clocks. */
+ return -EINVAL;
+ }
+
+ strcr |= SSI_STCR_TFEN0;
+
+ writel(strcr, ssi->base + SSI_STCR);
+ writel(strcr, ssi->base + SSI_SRCR);
+ writel(scr, ssi->base + SSI_SCR);
+
+ 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)
+{
+ 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;
+ }
+
+ writel(scr, ssi->base + 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 *ssi = cpu_dai->private_data;
+ u32 stccr, srccr;
+
+ stccr = readl(ssi->base + SSI_STCCR);
+ srccr = readl(ssi->base + SSI_SRCCR);
+
+ 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;
+ }
+
+ writel(stccr, ssi->base + SSI_STCCR);
+ writel(srccr, ssi->base + SSI_SRCCR);
+
+ return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+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;
+ u32 reg, sccr;
+
+ /* Tx/Rx config */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg = SSI_STCCR;
+ cpu_dai->dma_data = &ssi->dma_params_tx;
+ } else {
+ reg = SSI_SRCCR;
+ cpu_dai->dma_data = &ssi->dma_params_rx;
+ }
+
+ 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;
+ }
+
+ writel(sccr, ssi->base + reg);
+
+ return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *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;
+
+ 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;
+ }
+
+ 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)
+ scr |= SSI_SCR_TE;
+ 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_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;
+ }
+
+ 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);
+
+ 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)
+{
+ 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;
+
+ return 0;
+}
+
+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)
+{
+
+ 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 (dai->capture.channels_min) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void imx_pcm_free(struct snd_pcm *pcm)
+{
+ 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);
+
+ writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
+
+ 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);
+}
+
+static struct imx_ssi *ac97_ssi;
+
+static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ 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);
+
+ lreg = reg << 12;
+ writel(lreg, base + SSI_SACADD);
+
+ lval = val << 4;
+ writel(lval , base + SSI_SACDAT);
+
+ writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
+ udelay(100);
+}
+
+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);
+
+ return val;
+}
+
+static void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct imx_ssi *imx_ssi = ac97_ssi;
+
+ if (imx_ssi->ac97_reset)
+ imx_ssi->ac97_reset(ac97);
+}
+
+static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct imx_ssi *imx_ssi = ac97_ssi;
+
+ if (imx_ssi->ac97_warm_reset)
+ imx_ssi->ac97_warm_reset(ac97);
+}
+
+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
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+struct snd_soc_dai imx_ssi_pcm_dai[2];
+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+
+static int imx_ssi_probe(struct platform_device *pdev)
+{
+ 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;
+
+ ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
+ if (!ssi)
+ return -ENOMEM;
+
+ if (pdata) {
+ ssi->ac97_reset = pdata->ac97_reset;
+ ssi->ac97_warm_reset = pdata->ac97_warm_reset;
+ ssi->flags = pdata->flags;
+ }
+
+ ssi->irq = platform_get_irq(pdev, 0);
+
+ 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);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto failed_get_resource;
+ }
+
+ 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;
+ }
+
+ ssi->base = ioremap(res->start, resource_size(res));
+ if (!ssi->base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENODEV;
+ goto failed_ioremap;
+ }
+
+ 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;
+ 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;
+ }
+
+ platform_set_drvdata(pdev, ssi);
+
+ 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);
+
+ return ret;
+}
+
+static int __devexit imx_ssi_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);
+
+ if (ssi->flags & IMX_SSI_USE_AC97)
+ ac97_ssi = NULL;
+
+ if (!(ssi->flags & IMX_SSI_DMA))
+ imx_ssi_fiq_exit(pdev, ssi);
+
+ iounmap(ssi->base);
+ release_mem_region(res->start, resource_size(res));
+ clk_disable(ssi->clk);
+ clk_put(ssi->clk);
+ kfree(ssi);
+
+ return 0;
+}
+
+static struct platform_driver imx_ssi_driver = {
+ .probe = imx_ssi_probe,
+ .remove = __devexit_p(imx_ssi_remove),
+
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+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;
+}
+
+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_LICENSE("GPL");
+
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h
new file mode 100644
index 000000000000..55f26ebcd8c2
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.h
@@ -0,0 +1,237 @@
+/*
+ * 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 _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
+
+/* SSI clock sources */
+#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;
+
+ 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);
+
+ struct imx_pcm_dma_params dma_params_rx;
+ struct imx_pcm_dma_params dma_params_tx;
+
+ int enabled;
+};
+
+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
+
+#endif /* _IMX_SSI_H */
diff --git a/sound/soc/imx/mx1_mx2-pcm.c b/sound/soc/imx/mx1_mx2-pcm.c
deleted file mode 100644
index bffffcd5ff34..000000000000
--- a/sound/soc/imx/mx1_mx2-pcm.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * mx1_mx2-pcm.c -- ALSA SoC interface for Freescale i.MX1x, i.MX2x CPUs
- *
- * Copyright 2009 Vista Silicon S.L.
- * Author: Javier Martin
- * javier.martin@vista-silicon.com
- *
- * Based on mxc-pcm.c by Liam Girdwood.
- *
- * 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 <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <asm/dma.h>
-#include <mach/hardware.h>
-#include <mach/dma-mx1-mx2.h>
-
-#include "mx1_mx2-pcm.h"
-
-
-static const struct snd_pcm_hardware mx1_mx2_pcm_hardware = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .buffer_bytes_max = 32 * 1024,
- .period_bytes_min = 64,
- .period_bytes_max = 8 * 1024,
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-struct mx1_mx2_runtime_data {
- int dma_ch;
- int active;
- unsigned int period;
- unsigned int periods;
- int tx_spin;
- spinlock_t dma_lock;
- struct mx1_mx2_pcm_dma_params *dma_params;
-};
-
-
-/**
- * This function stops the current dma transfer for playback
- * and clears the dma pointers.
- *
- * @param substream pointer to the structure of the current stream.
- *
- */
-static int audio_stop_dma(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
- unsigned long flags;
-
- spin_lock_irqsave(&prtd->dma_lock, flags);
-
- pr_debug("%s\n", __func__);
-
- prtd->active = 0;
- prtd->period = 0;
- prtd->periods = 0;
-
- /* this stops the dma channel and clears the buffer ptrs */
-
- imx_dma_disable(prtd->dma_ch);
-
- spin_unlock_irqrestore(&prtd->dma_lock, flags);
-
- return 0;
-}
-
-/**
- * This function is called whenever a new audio block needs to be
- * transferred to the codec. The function receives the address and the size
- * of the new block and start a new DMA transfer.
- *
- * @param substream pointer to the structure of the current stream.
- *
- */
-static int dma_new_period(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
- unsigned int dma_size;
- unsigned int offset;
- int ret = 0;
- dma_addr_t mem_addr;
- unsigned int dev_addr;
-
- if (prtd->active) {
- dma_size = frames_to_bytes(runtime, runtime->period_size);
- offset = dma_size * prtd->period;
-
- pr_debug("%s: period (%d) out of (%d)\n", __func__,
- prtd->period,
- runtime->periods);
- pr_debug("period_size %d frames\n offset %d bytes\n",
- (unsigned int)runtime->period_size,
- offset);
- pr_debug("dma_size %d bytes\n", dma_size);
-
- snd_BUG_ON(dma_size > mx1_mx2_pcm_hardware.period_bytes_max);
-
- mem_addr = (dma_addr_t)(runtime->dma_addr + offset);
- dev_addr = prtd->dma_params->per_address;
- pr_debug("%s: mem_addr is %x\n dev_addr is %x\n",
- __func__, mem_addr, dev_addr);
-
- ret = imx_dma_setup_single(prtd->dma_ch, mem_addr,
- dma_size, dev_addr,
- prtd->dma_params->transfer_type);
- if (ret < 0) {
- printk(KERN_ERR "Error %d configuring DMA\n", ret);
- return ret;
- }
- imx_dma_enable(prtd->dma_ch);
-
- pr_debug("%s: transfer enabled\nmem_addr = %x\n",
- __func__, (unsigned int) mem_addr);
- pr_debug("dev_addr = %x\ndma_size = %d\n",
- (unsigned int) dev_addr, dma_size);
-
- prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
- prtd->period++;
- prtd->period %= runtime->periods;
- }
- return ret;
-}
-
-
-/**
- * This is a callback which will be called
- * when a TX transfer finishes. The call occurs
- * in interrupt context.
- *
- * @param dat pointer to the structure of the current stream.
- *
- */
-static void audio_dma_irq(int channel, void *data)
-{
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- struct mx1_mx2_runtime_data *prtd;
- unsigned int dma_size;
- unsigned int previous_period;
- unsigned int offset;
-
- substream = data;
- runtime = substream->runtime;
- prtd = runtime->private_data;
- previous_period = prtd->periods;
- dma_size = frames_to_bytes(runtime, runtime->period_size);
- offset = dma_size * previous_period;
-
- prtd->tx_spin = 0;
- prtd->periods++;
- prtd->periods %= runtime->periods;
-
- pr_debug("%s: irq per %d offset %x\n", __func__, prtd->periods, offset);
-
- /*
- * If we are getting a callback for an active stream then we inform
- * the PCM middle layer we've finished a period
- */
- if (prtd->active)
- snd_pcm_period_elapsed(substream);
-
- /*
- * Trig next DMA transfer
- */
- dma_new_period(substream);
-}
-
-/**
- * This function configures the hardware to allow audio
- * playback operations. It is called by ALSA framework.
- *
- * @param substream pointer to the structure of the current stream.
- *
- * @return 0 on success, -1 otherwise.
- */
-static int
-snd_mx1_mx2_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
- prtd->period = 0;
- prtd->periods = 0;
-
- return 0;
-}
-
-static int mx1_mx2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
-
- ret = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0) {
- printk(KERN_ERR "%s: Error %d failed to malloc pcm pages \n",
- __func__, ret);
- return ret;
- }
-
- pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_addr 0x(%x)\n",
- __func__, (unsigned int)runtime->dma_addr);
- pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_area 0x(%x)\n",
- __func__, (unsigned int)runtime->dma_area);
- pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_bytes 0x(%x)\n",
- __func__, (unsigned int)runtime->dma_bytes);
-
- return ret;
-}
-
-static int mx1_mx2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
- imx_dma_free(prtd->dma_ch);
-
- snd_pcm_lib_free_pages(substream);
-
- return 0;
-}
-
-static int mx1_mx2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct mx1_mx2_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- prtd->tx_spin = 0;
- /* requested stream startup */
- prtd->active = 1;
- pr_debug("%s: starting dma_new_period\n", __func__);
- ret = dma_new_period(substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- /* requested stream shutdown */
- pr_debug("%s: stopping dma transfer\n", __func__);
- ret = audio_stop_dma(substream);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static snd_pcm_uframes_t
-mx1_mx2_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
- unsigned int offset = 0;
-
- /* tx_spin value is used here to check if a transfer is active */
- if (prtd->tx_spin) {
- offset = (runtime->period_size * (prtd->periods)) +
- (runtime->period_size >> 1);
- if (offset >= runtime->buffer_size)
- offset = runtime->period_size >> 1;
- } else {
- offset = (runtime->period_size * (prtd->periods));
- if (offset >= runtime->buffer_size)
- offset = 0;
- }
- pr_debug("%s: pointer offset %x\n", __func__, offset);
-
- return offset;
-}
-
-static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct mx1_mx2_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
- int ret;
-
- snd_soc_set_runtime_hwparams(substream, &mx1_mx2_pcm_hardware);
-
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- return ret;
-
- prtd = kzalloc(sizeof(struct mx1_mx2_runtime_data), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
-
- runtime->private_data = prtd;
-
- if (!dma_data)
- return -ENODEV;
-
- prtd->dma_params = dma_data;
-
- pr_debug("%s: Requesting dma channel (%s)\n", __func__,
- prtd->dma_params->name);
- ret = imx_dma_request_by_prio(prtd->dma_params->name, DMA_PRIO_HIGH);
- if (ret < 0) {
- printk(KERN_ERR "Error %d requesting dma channel\n", ret);
- return ret;
- }
- prtd->dma_ch = ret;
- imx_dma_config_burstlen(prtd->dma_ch,
- prtd->dma_params->watermark_level);
-
- ret = imx_dma_config_channel(prtd->dma_ch,
- prtd->dma_params->per_config,
- prtd->dma_params->mem_config,
- prtd->dma_params->event_id, 0);
-
- if (ret) {
- pr_debug(KERN_ERR "Error %d configuring dma channel %d\n",
- ret, prtd->dma_ch);
- return ret;
- }
-
- pr_debug("%s: Setting tx dma callback function\n", __func__);
- ret = imx_dma_setup_handlers(prtd->dma_ch,
- audio_dma_irq, NULL,
- (void *)substream);
- if (ret < 0) {
- printk(KERN_ERR "Error %d setting dma callback function\n", ret);
- return ret;
- }
- return 0;
-
- out:
- return ret;
-}
-
-static int mx1_mx2_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
- kfree(prtd);
-
- return 0;
-}
-
-static int mx1_mx2_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops mx1_mx2_pcm_ops = {
- .open = mx1_mx2_pcm_open,
- .close = mx1_mx2_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = mx1_mx2_pcm_hw_params,
- .hw_free = mx1_mx2_pcm_hw_free,
- .prepare = snd_mx1_mx2_prepare,
- .trigger = mx1_mx2_pcm_trigger,
- .pointer = mx1_mx2_pcm_pointer,
- .mmap = mx1_mx2_pcm_mmap,
-};
-
-static u64 mx1_mx2_pcm_dmamask = 0xffffffff;
-
-static int mx1_mx2_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;
- size_t size = mx1_mx2_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
-
- /* Reserve uncached-buffered memory area for DMA */
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
-
- pr_debug("%s: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- __func__, (void *) buf->area, (void *) buf->addr, size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
-
-static void mx1_mx2_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- 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;
- }
-}
-
-static int mx1_mx2_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 = &mx1_mx2_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (dai->playback.channels_min) {
- ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- pr_debug("%s: preallocate playback buffer\n", __func__);
- if (ret)
- goto out;
- }
-
- if (dai->capture.channels_min) {
- ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- pr_debug("%s: preallocate capture buffer\n", __func__);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-struct snd_soc_platform mx1_mx2_soc_platform = {
- .name = "mx1_mx2-audio",
- .pcm_ops = &mx1_mx2_pcm_ops,
- .pcm_new = mx1_mx2_pcm_new,
- .pcm_free = mx1_mx2_pcm_free_dma_buffers,
-};
-EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform);
-
-static int __init mx1_mx2_soc_platform_init(void)
-{
- return snd_soc_register_platform(&mx1_mx2_soc_platform);
-}
-module_init(mx1_mx2_soc_platform_init);
-
-static void __exit mx1_mx2_soc_platform_exit(void)
-{
- snd_soc_unregister_platform(&mx1_mx2_soc_platform);
-}
-module_exit(mx1_mx2_soc_platform_exit);
-
-MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
-MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mx1_mx2-pcm.h b/sound/soc/imx/mx1_mx2-pcm.h
deleted file mode 100644
index 2e528106570b..000000000000
--- a/sound/soc/imx/mx1_mx2-pcm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * mx1_mx2-pcm.h :- ASoC platform header for Freescale i.MX1x, i.MX2x
- *
- * 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 _MX1_MX2_PCM_H
-#define _MX1_MX2_PCM_H
-
-/* DMA information for mx1_mx2 platforms */
-struct mx1_mx2_pcm_dma_params {
- char *name; /* stream identifier */
- unsigned int transfer_type; /* READ or WRITE DMA transfer */
- dma_addr_t per_address; /* physical address of SSI fifo */
- int event_id; /* fixed DMA number for SSI fifo */
- int watermark_level; /* SSI fifo watermark level */
- int per_config; /* DMA Config flags for peripheral */
- int mem_config; /* DMA Config flags for RAM */
- };
-
-/* platform data */
-extern struct snd_soc_platform mx1_mx2_soc_platform;
-
-#endif
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
deleted file mode 100644
index 07d2a248438c..000000000000
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * mx27vis_wm8974.c -- SoC audio for mx27vis
- *
- * Copyright 2009 Vista Silicon S.L.
- * Author: Javier Martin
- * javier.martin@vista-silicon.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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-
-#include "../codecs/wm8974.h"
-#include "mx1_mx2-pcm.h"
-#include "mxc-ssi.h"
-#include <mach/gpio.h>
-#include <mach/iomux.h>
-
-#define IGNORED_ARG 0
-
-
-static struct snd_soc_card mx27vis;
-
-/**
- * This function connects SSI1 (HPCR1) as slave to
- * SSI1 external signals (PPCR1)
- * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from
- * port 4
- */
-void audmux_connect_1_4(void)
-{
- pr_debug("AUDMUX: normal operation mode\n");
- /* Reset HPCR1 and PPCR1 */
-
- DAM_HPCR1 = 0x00000000;
- DAM_PPCR1 = 0x00000000;
-
- /* set to synchronous */
- DAM_HPCR1 |= AUDMUX_HPCR_SYN;
- DAM_PPCR1 |= AUDMUX_PPCR_SYN;
-
-
- /* set Rx sources 1 <--> 4 */
- DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */
- DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */
-
- /* set Tx frame and Clock direction and source 4 --> 1 output */
- DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR;
- DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */
-
- return;
-}
-
-static int mx27vis_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 *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0;
- int ret = 0;
-
- /*
- * The WM8974 is better at generating accurate audio clocks than the
- * MX27 SSI controller, so we will use it as master when we can.
- */
- switch (params_rate(params)) {
- case 8000:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- mclk = WM8974_MCLKDIV_12;
- pll_out = 24576000;
- break;
- case 16000:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- pll_out = 12288000;
- break;
- case 48000:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_4;
- pll_out = 12288000;
- break;
- case 96000:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_2;
- pll_out = 12288000;
- break;
- case 11025:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_16;
- pll_out = 11289600;
- break;
- case 22050:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_8;
- pll_out = 11289600;
- break;
- case 44100:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_4;
- mclk = WM8974_MCLKDIV_2;
- pll_out = 11289600;
- break;
- case 88200:
- fmt = SND_SOC_DAIFMT_CBM_CFM;
- bclk = WM8974_BCLKDIV_2;
- pll_out = 11289600;
- break;
- }
-
- /* set codec DAI configuration */
- ret = codec_dai->ops->set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_SYNC | fmt);
- if (ret < 0) {
- printk(KERN_ERR "Error from codec DAI configuration\n");
- return ret;
- }
-
- /* set cpu DAI configuration */
- ret = cpu_dai->ops->set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_SYNC | fmt);
- if (ret < 0) {
- printk(KERN_ERR "Error from cpu DAI configuration\n");
- return ret;
- }
-
- /* Put DC field of STCCR to 1 (not zero) */
- ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2);
-
- /* set the SSI system clock as input */
- ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "Error when setting system SSI clk\n");
- return ret;
- }
-
- /* set codec BCLK division for sample rate */
- ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk);
- if (ret < 0) {
- printk(KERN_ERR "Error when setting BCLK division\n");
- return ret;
- }
-
-
- /* codec PLL input is 25 MHz */
- ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
- 25000000, pll_out);
- if (ret < 0) {
- printk(KERN_ERR "Error when setting PLL input\n");
- return ret;
- }
-
- /*set codec MCLK division for sample rate */
- ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk);
- if (ret < 0) {
- printk(KERN_ERR "Error when setting MCLK division\n");
- return ret;
- }
-
- return 0;
-}
-
-static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-
- /* disable the PLL */
- return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
- 0, 0);
-}
-
-/*
- * mx27vis WM8974 HiFi DAI opserations.
- */
-static struct snd_soc_ops mx27vis_hifi_ops = {
- .hw_params = mx27vis_hifi_hw_params,
- .hw_free = mx27vis_hifi_hw_free,
-};
-
-
-static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
-{
- return 0;
-}
-
-static int mx27vis_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int mx27vis_probe(struct platform_device *pdev)
-{
- int ret = 0;
-
- ret = get_ssi_clk(0, &pdev->dev);
-
- if (ret < 0) {
- printk(KERN_ERR "%s: cant get ssi clock\n", __func__);
- return ret;
- }
-
-
- return 0;
-}
-
-static int mx27vis_remove(struct platform_device *pdev)
-{
- put_ssi_clk(0);
- return 0;
-}
-
-static struct snd_soc_dai_link mx27vis_dai[] = {
-{ /* Hifi Playback*/
- .name = "WM8974",
- .stream_name = "WM8974 HiFi",
- .cpu_dai = &imx_ssi_pcm_dai[0],
- .codec_dai = &wm8974_dai,
- .ops = &mx27vis_hifi_ops,
-},
-};
-
-static struct snd_soc_card mx27vis = {
- .name = "mx27vis",
- .platform = &mx1_mx2_soc_platform,
- .probe = mx27vis_probe,
- .remove = mx27vis_remove,
- .suspend_pre = mx27vis_suspend,
- .resume_post = mx27vis_resume,
- .dai_link = mx27vis_dai,
- .num_links = ARRAY_SIZE(mx27vis_dai),
-};
-
-static struct snd_soc_device mx27vis_snd_devdata = {
- .card = &mx27vis,
- .codec_dev = &soc_codec_dev_wm8974,
-};
-
-static struct platform_device *mx27vis_snd_device;
-
-/* Temporal definition of board specific behaviour */
-void gpio_ssi_active(int ssi_num)
-{
- int ret = 0;
-
- unsigned int ssi1_pins[] = {
- PC20_PF_SSI1_FS,
- PC21_PF_SSI1_RXD,
- PC22_PF_SSI1_TXD,
- PC23_PF_SSI1_CLK,
- };
- unsigned int ssi2_pins[] = {
- PC24_PF_SSI2_FS,
- PC25_PF_SSI2_RXD,
- PC26_PF_SSI2_TXD,
- PC27_PF_SSI2_CLK,
- };
- if (ssi_num == 0)
- ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
- ARRAY_SIZE(ssi1_pins), "USB OTG");
- else
- ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
- ARRAY_SIZE(ssi2_pins), "USB OTG");
- if (ret)
- printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num);
-}
-
-
-static int __init mx27vis_init(void)
-{
- int ret;
-
- mx27vis_snd_device = platform_device_alloc("soc-audio", -1);
- if (!mx27vis_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata);
- mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev;
- ret = platform_device_add(mx27vis_snd_device);
-
- if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- platform_device_put(mx27vis_snd_device);
- }
-
- /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */
- gpio_ssi_active(0);
- audmux_connect_1_4();
-
- return ret;
-}
-
-static void __exit mx27vis_exit(void)
-{
- /* We should call some "ssi_gpio_inactive()" properly */
-}
-
-module_init(mx27vis_init);
-module_exit(mx27vis_exit);
-
-
-MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
-MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c
deleted file mode 100644
index ccdefe60e752..000000000000
--- a/sound/soc/imx/mxc-ssi.c
+++ /dev/null
@@ -1,860 +0,0 @@
-/*
- * mxc-ssi.c -- SSI driver for Freescale IMX
- *
- * Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- * Based 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * TODO:
- * Need to rework SSI register defs when new defs go into mainline.
- * Add support for TDM and FIFO 1.
- * Add support for i.mx3x DMA interface.
- *
- */
-
-
-#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/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <mach/dma-mx1-mx2.h>
-#include <asm/mach-types.h>
-
-#include "mxc-ssi.h"
-#include "mx1_mx2-pcm.h"
-
-#define SSI1_PORT 0
-#define SSI2_PORT 1
-
-static int ssi_active[2] = {0, 0};
-
-/* DMA information for mx1_mx2 platforms */
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
- .name = "SSI1 PCM Stereo out 0",
- .transfer_type = DMA_MODE_WRITE,
- .per_address = SSI1_BASE_ADDR + STX0,
- .event_id = DMA_REQ_SSI1_TX0,
- .watermark_level = TXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
- .name = "SSI1 PCM Stereo out 1",
- .transfer_type = DMA_MODE_WRITE,
- .per_address = SSI1_BASE_ADDR + STX1,
- .event_id = DMA_REQ_SSI1_TX1,
- .watermark_level = TXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
- .name = "SSI1 PCM Stereo in 0",
- .transfer_type = DMA_MODE_READ,
- .per_address = SSI1_BASE_ADDR + SRX0,
- .event_id = DMA_REQ_SSI1_RX0,
- .watermark_level = RXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
- .name = "SSI1 PCM Stereo in 1",
- .transfer_type = DMA_MODE_READ,
- .per_address = SSI1_BASE_ADDR + SRX1,
- .event_id = DMA_REQ_SSI1_RX1,
- .watermark_level = RXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
- .name = "SSI2 PCM Stereo out 0",
- .transfer_type = DMA_MODE_WRITE,
- .per_address = SSI2_BASE_ADDR + STX0,
- .event_id = DMA_REQ_SSI2_TX0,
- .watermark_level = TXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
- .name = "SSI2 PCM Stereo out 1",
- .transfer_type = DMA_MODE_WRITE,
- .per_address = SSI2_BASE_ADDR + STX1,
- .event_id = DMA_REQ_SSI2_TX1,
- .watermark_level = TXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
- .name = "SSI2 PCM Stereo in 0",
- .transfer_type = DMA_MODE_READ,
- .per_address = SSI2_BASE_ADDR + SRX0,
- .event_id = DMA_REQ_SSI2_RX0,
- .watermark_level = RXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
- .name = "SSI2 PCM Stereo in 1",
- .transfer_type = DMA_MODE_READ,
- .per_address = SSI2_BASE_ADDR + SRX1,
- .event_id = DMA_REQ_SSI2_RX1,
- .watermark_level = RXFIFO_WATERMARK,
- .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
- .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct clk *ssi_clk0, *ssi_clk1;
-
-int get_ssi_clk(int ssi, struct device *dev)
-{
- switch (ssi) {
- case 0:
- ssi_clk0 = clk_get(dev, "ssi1");
- if (IS_ERR(ssi_clk0))
- return PTR_ERR(ssi_clk0);
- return 0;
- case 1:
- ssi_clk1 = clk_get(dev, "ssi2");
- if (IS_ERR(ssi_clk1))
- return PTR_ERR(ssi_clk1);
- return 0;
- default:
- return -EINVAL;
- }
-}
-EXPORT_SYMBOL(get_ssi_clk);
-
-void put_ssi_clk(int ssi)
-{
- switch (ssi) {
- case 0:
- clk_put(ssi_clk0);
- ssi_clk0 = NULL;
- break;
- case 1:
- clk_put(ssi_clk1);
- ssi_clk1 = NULL;
- break;
- }
-}
-EXPORT_SYMBOL(put_ssi_clk);
-
-/*
- * 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)
-{
- u32 scr;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- scr = SSI1_SCR;
- pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr);
- } else {
- scr = SSI2_SCR;
- pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr);
- }
-
- if (scr & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
-
- switch (clk_id) {
- case IMX_SSP_SYS_CLK:
- if (dir == SND_SOC_CLOCK_OUT) {
- scr |= SSI_SCR_SYS_CLK_EN;
- pr_debug("%s: clk of is output\n", __func__);
- } else {
- scr &= ~SSI_SCR_SYS_CLK_EN;
- pr_debug("%s: clk of is input\n", __func__);
- }
- break;
- default:
- return -EINVAL;
- }
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- pr_debug("%s: writeback of SSI1_SCR\n", __func__);
- SSI1_SCR = scr;
- } else {
- pr_debug("%s: writeback of SSI2_SCR\n", __func__);
- SSI2_SCR = 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)
-{
- u32 stccr, srccr;
-
- pr_debug("%s\n", __func__);
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- if (SSI1_SCR & SSI_SCR_SSIEN)
- return 0;
- srccr = SSI1_STCCR;
- stccr = SSI1_STCCR;
- } else {
- if (SSI2_SCR & SSI_SCR_SSIEN)
- return 0;
- srccr = SSI2_STCCR;
- stccr = SSI2_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;
- }
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- SSI1_STCCR = stccr;
- SSI1_SRCCR = srccr;
- } else {
- SSI2_STCCR = stccr;
- SSI2_SRCCR = 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 mask, int slots)
-{
- u32 stmsk, srmsk, stccr;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- if (SSI1_SCR & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
- stccr = SSI1_STCCR;
- } else {
- if (SSI2_SCR & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
- stccr = SSI2_STCCR;
- }
-
- stmsk = srmsk = mask;
- stccr &= ~SSI_STCCR_DC_MASK;
- stccr |= SSI_STCCR_DC(slots - 1);
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- SSI1_STMSK = stmsk;
- SSI1_SRMSK = srmsk;
- SSI1_SRCCR = SSI1_STCCR = stccr;
- } else {
- SSI2_STMSK = stmsk;
- SSI2_SRMSK = srmsk;
- SSI2_SRCCR = SSI2_STCCR = stccr;
- }
-
- return 0;
-}
-
-/*
- * 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.
- */
-static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- u32 stcr = 0, srcr = 0, scr;
-
- /*
- * This is done to avoid this function to modify
- * previous set values in stcr
- */
- stcr = SSI1_STCR;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
- scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
- else
- scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
-
- if (scr & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- 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 */
- 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 */
- 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 */
- 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 */
- 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:
- stcr |= SSI_STCR_TFSI;
- stcr &= ~SSI_STCR_TSCKP;
- srcr |= SSI_SRCR_RFSI;
- srcr &= ~SSI_SRCR_RSCKP;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
- srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
- break;
- case SND_SOC_DAIFMT_NB_IF:
- stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
- srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- stcr &= ~SSI_STCR_TFSI;
- stcr |= SSI_STCR_TSCKP;
- srcr &= ~SSI_SRCR_RFSI;
- srcr |= 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;
- srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
- 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;
- }
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- SSI1_STCR = stcr;
- SSI1_SRCR = srcr;
- SSI1_SCR = scr;
- } else {
- SSI2_STCR = stcr;
- SSI2_SRCR = srcr;
- SSI2_SCR = scr;
- }
-
- return 0;
-}
-
-static int imx_ssi_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* set up TX DMA params */
- switch (cpu_dai->id) {
- case IMX_DAI_SSI0:
- cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
- break;
- case IMX_DAI_SSI1:
- cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
- break;
- case IMX_DAI_SSI2:
- cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
- break;
- case IMX_DAI_SSI3:
- cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
- }
- pr_debug("%s: (playback)\n", __func__);
- } else {
- /* set up RX DMA params */
- switch (cpu_dai->id) {
- case IMX_DAI_SSI0:
- cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
- break;
- case IMX_DAI_SSI1:
- cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
- break;
- case IMX_DAI_SSI2:
- cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
- break;
- case IMX_DAI_SSI3:
- cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
- }
- pr_debug("%s: (capture)\n", __func__);
- }
-
- /*
- * we cant really change any SSI values after SSI is enabled
- * need to fix in software for max flexibility - lrg
- */
- if (cpu_dai->active) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
-
- /* reset the SSI port - Sect 45.4.4 */
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-
- if (!ssi_clk0)
- return -EINVAL;
-
- if (ssi_active[SSI1_PORT]++) {
- pr_debug("%s: exit before reset\n", __func__);
- return 0;
- }
-
- /* SSI1 Reset */
- SSI1_SCR = 0;
-
- SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
- SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
- SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
- SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
- } else {
-
- if (!ssi_clk1)
- return -EINVAL;
-
- if (ssi_active[SSI2_PORT]++) {
- pr_debug("%s: exit before reset\n", __func__);
- return 0;
- }
-
- /* SSI2 Reset */
- SSI2_SCR = 0;
-
- SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
- SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
- SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
- SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
- }
-
- return 0;
-}
-
-int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- u32 stccr, stcr, sier;
-
- pr_debug("%s\n", __func__);
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
- stcr = SSI1_STCR;
- sier = SSI1_SIER;
- } else {
- stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
- stcr = SSI2_STCR;
- sier = SSI2_SIER;
- }
-
- /* DAI data (word) size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- stccr |= SSI_STCCR_WL(16);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- stccr |= SSI_STCCR_WL(20);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- stccr |= SSI_STCCR_WL(24);
- break;
- }
-
- /* enable interrupts */
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
- stcr |= SSI_STCR_TFEN0;
- else
- stcr |= SSI_STCR_TFEN1;
- sier |= SSI_SIER_TDMAE;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- SSI1_STCR = stcr;
- SSI1_STCCR = stccr;
- SSI1_SIER = sier;
- } else {
- SSI2_STCR = stcr;
- SSI2_STCCR = stccr;
- SSI2_SIER = sier;
- }
-
- return 0;
-}
-
-int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- u32 srccr, srcr, sier;
-
- pr_debug("%s\n", __func__);
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
- srcr = SSI1_SRCR;
- sier = SSI1_SIER;
- } else {
- srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
- srcr = SSI2_SRCR;
- sier = SSI2_SIER;
- }
-
- /* DAI data (word) size */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- srccr |= SSI_SRCCR_WL(16);
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- srccr |= SSI_SRCCR_WL(20);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- srccr |= SSI_SRCCR_WL(24);
- break;
- }
-
- /* enable interrupts */
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
- srcr |= SSI_SRCR_RFEN0;
- else
- srcr |= SSI_SRCR_RFEN1;
- sier |= SSI_SIER_RDMAE;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- SSI1_SRCR = srcr;
- SSI1_SRCCR = srccr;
- SSI1_SIER = sier;
- } else {
- SSI2_SRCR = srcr;
- SSI2_SRCCR = srccr;
- SSI2_SIER = sier;
- }
-
- return 0;
-}
-
-/*
- * Should only be called when port is inactive (i.e. SSIEN = 0),
- * although can be called multiple times by upper layers.
- */
-int imx_ssi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
- int ret;
-
- /* cant change any parameters when SSI is running */
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- if (SSI1_SCR & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
- } else {
- if (SSI2_SCR & SSI_SCR_SSIEN) {
- printk(KERN_WARNING "Warning ssi already enabled\n");
- return 0;
- }
- }
-
- /*
- * Configure both tx and rx params with the same settings. This is
- * really a harware restriction because SSI must be disabled until
- * we can change those values. If there is an active audio stream in
- * one direction, enabling the other direction with different
- * settings would mean disturbing the running one.
- */
- ret = imx_ssi_hw_tx_params(substream, params);
- if (ret < 0)
- return ret;
- return imx_ssi_hw_rx_params(substream, params);
-}
-
-int imx_ssi_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret;
-
- pr_debug("%s\n", __func__);
-
- /* Enable clks here to follow SSI recommended init sequence */
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
- ret = clk_enable(ssi_clk0);
- if (ret < 0)
- printk(KERN_ERR "Unable to enable ssi_clk0\n");
- } else {
- ret = clk_enable(ssi_clk1);
- if (ret < 0)
- printk(KERN_ERR "Unable to enable ssi_clk1\n");
- }
-
- return 0;
-}
-
-static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- u32 scr;
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
- scr = SSI1_SCR;
- else
- scr = SSI2_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)
- scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
- else
- scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
- break;
- 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;
- break;
- default:
- return -EINVAL;
- }
-
- if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
- SSI1_SCR = scr;
- else
- SSI2_SCR = scr;
-
- return 0;
-}
-
-static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
- /* shutdown SSI if neither Tx or Rx is active */
- if (!cpu_dai->active) {
-
- if (cpu_dai->id == IMX_DAI_SSI0 ||
- cpu_dai->id == IMX_DAI_SSI2) {
-
- if (--ssi_active[SSI1_PORT] > 1)
- return;
-
- SSI1_SCR = 0;
- clk_disable(ssi_clk0);
- } else {
- if (--ssi_active[SSI2_PORT])
- return;
- SSI2_SCR = 0;
- clk_disable(ssi_clk1);
- }
- }
-}
-
-#ifdef CONFIG_PM
-static int imx_ssi_suspend(struct platform_device *dev,
- struct snd_soc_dai *dai)
-{
- return 0;
-}
-
-static int imx_ssi_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
-{
- return 0;
-}
-
-#else
-#define imx_ssi_suspend NULL
-#define imx_ssi_resume NULL
-#endif
-
-#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_BITS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
-
-static struct snd_soc_dai_ops imx_ssi_pcm_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,
-};
-
-struct snd_soc_dai imx_ssi_pcm_dai[] = {
-{
- .name = "imx-i2s-1-0",
- .id = IMX_DAI_SSI0,
- .suspend = imx_ssi_suspend,
- .resume = imx_ssi_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .ops = &imx_ssi_pcm_dai_ops,
-},
-{
- .name = "imx-i2s-2-0",
- .id = IMX_DAI_SSI1,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .ops = &imx_ssi_pcm_dai_ops,
-},
-{
- .name = "imx-i2s-1-1",
- .id = IMX_DAI_SSI2,
- .suspend = imx_ssi_suspend,
- .resume = imx_ssi_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .ops = &imx_ssi_pcm_dai_ops,
-},
-{
- .name = "imx-i2s-2-1",
- .id = IMX_DAI_SSI3,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .formats = IMX_SSI_BITS,
- .rates = IMX_SSI_RATES,},
- .ops = &imx_ssi_pcm_dai_ops,
-},
-};
-EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
-
-static int __init imx_ssi_init(void)
-{
- return snd_soc_register_dais(imx_ssi_pcm_dai,
- ARRAY_SIZE(imx_ssi_pcm_dai));
-}
-
-static void __exit imx_ssi_exit(void)
-{
- snd_soc_unregister_dais(imx_ssi_pcm_dai,
- ARRAY_SIZE(imx_ssi_pcm_dai));
-}
-
-module_init(imx_ssi_init);
-module_exit(imx_ssi_exit);
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
-MODULE_DESCRIPTION("i.MX ASoC I2S driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h
deleted file mode 100644
index 12bbdc9c7ecd..000000000000
--- a/sound/soc/imx/mxc-ssi.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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 _IMX_SSI_H
-#define _IMX_SSI_H
-
-#include <mach/hardware.h>
-
-/* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/ when stable */
-#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR)
-#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR)
-
-#define STX0 0x00
-#define STX1 0x04
-#define SRX0 0x08
-#define SRX1 0x0c
-#define SCR 0x10
-#define SISR 0x14
-#define SIER 0x18
-#define STCR 0x1c
-#define SRCR 0x20
-#define STCCR 0x24
-#define SRCCR 0x28
-#define SFCSR 0x2c
-#define STR 0x30
-#define SOR 0x34
-#define SACNT 0x38
-#define SACADD 0x3c
-#define SACDAT 0x40
-#define SATAG 0x44
-#define STMSK 0x48
-#define SRMSK 0x4c
-
-#define SSI1_STX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0)))
-#define SSI1_STX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1)))
-#define SSI1_SRX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0)))
-#define SSI1_SRX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1)))
-#define SSI1_SCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR)))
-#define SSI1_SISR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR)))
-#define SSI1_SIER (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER)))
-#define SSI1_STCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR)))
-#define SSI1_SRCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR)))
-#define SSI1_STCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR)))
-#define SSI1_SRCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR)))
-#define SSI1_SFCSR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR)))
-#define SSI1_STR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR)))
-#define SSI1_SOR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR)))
-#define SSI1_SACNT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT)))
-#define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD)))
-#define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT)))
-#define SSI1_SATAG (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG)))
-#define SSI1_STMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK)))
-#define SSI1_SRMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK)))
-
-
-#define SSI2_STX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0)))
-#define SSI2_STX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1)))
-#define SSI2_SRX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0)))
-#define SSI2_SRX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1)))
-#define SSI2_SCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR)))
-#define SSI2_SISR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR)))
-#define SSI2_SIER (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER)))
-#define SSI2_STCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR)))
-#define SSI2_SRCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR)))
-#define SSI2_STCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR)))
-#define SSI2_SRCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR)))
-#define SSI2_SFCSR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR)))
-#define SSI2_STR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR)))
-#define SSI2_SOR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR)))
-#define SSI2_SACNT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT)))
-#define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD)))
-#define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT)))
-#define SSI2_SATAG (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG)))
-#define SSI2_STMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK)))
-#define SSI2_SRMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK)))
-
-#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_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 (x << 4)
-#define SSI_SACNT_RD (x << 3)
-#define SSI_SACNT_TIF (x << 2)
-#define SSI_SACNT_FV (x << 1)
-#define SSI_SACNT_AC97EN (x << 0)
-
-/* Watermarks for FIFO's */
-#define TXFIFO_WATERMARK 0x4
-#define RXFIFO_WATERMARK 0x4
-
-/* 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 */
-
-/* SSI clock sources */
-#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
-
-
-/* SSI Div 2 */
-#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2)
-#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
-
-extern struct snd_soc_dai imx_ssi_pcm_dai[4];
-extern int get_ssi_clk(int ssi, struct device *dev);
-extern void put_ssi_clk(int ssi);
-#endif
diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c
new file mode 100644
index 000000000000..a8307d55c70e
--- /dev/null
+++ b/sound/soc/imx/phycore-ac97.c
@@ -0,0 +1,90 @@
+/*
+ * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode
+ *
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/wm9712.h"
+#include "imx-ssi.h"
+
+static struct snd_soc_card imx_phycore;
+
+static struct snd_soc_ops imx_phycore_hifi_ops = {
+};
+
+static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
+ {
+ .name = "HiFi",
+ .stream_name = "HiFi",
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ .ops = &imx_phycore_hifi_ops,
+ },
+};
+
+static struct snd_soc_card imx_phycore = {
+ .name = "PhyCORE-audio",
+ .platform = &imx_soc_platform,
+ .dai_link = imx_phycore_dai_ac97,
+ .num_links = ARRAY_SIZE(imx_phycore_dai_ac97),
+};
+
+static struct snd_soc_device imx_phycore_snd_devdata = {
+ .card = &imx_phycore,
+ .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *imx_phycore_snd_device;
+
+static int __init imx_phycore_init(void)
+{
+ int ret;
+
+ if (!machine_is_pcm043() && !machine_is_pca100())
+ /* return happy. We might run on a totally different machine */
+ return 0;
+
+ imx_phycore_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_phycore_snd_device)
+ return -ENOMEM;
+
+ imx_phycore_dai_ac97[0].cpu_dai = &imx_ssi_pcm_dai[0];
+
+ platform_set_drvdata(imx_phycore_snd_device, &imx_phycore_snd_devdata);
+ imx_phycore_snd_devdata.dev = &imx_phycore_snd_device->dev;
+ ret = platform_device_add(imx_phycore_snd_device);
+
+ if (ret) {
+ printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+ platform_device_put(imx_phycore_snd_device);
+ }
+
+ return ret;
+}
+
+static void __exit imx_phycore_exit(void)
+{
+ platform_device_unregister(imx_phycore_snd_device);
+}
+
+late_initcall(imx_phycore_init);
+module_exit(imx_phycore_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
+MODULE_LICENSE("GPL");