Patch submitted again.
Fixed a lot of coding style problems, debugging information now using "pr_debug()" and problem of freeing both dma's when closing pcm.
Signed-off-by: Javier Martin javier.martin@vista-silicon.com
diff --git a/sound/soc/imx/imx27-pcm.c b/sound/soc/imx/imx27-pcm.c new file mode 100644 index 0000000..ea00983 --- /dev/null +++ b/sound/soc/imx/imx27-pcm.c @@ -0,0 +1,555 @@ +/* + * linux/sound/arm/mx27-pcm.c -- ALSA SoC interface for the Freescale i.MX27 CPU + * + * Copyright 2009 Vista Silicon S.L. + * Author: Javier Martin + * javier.martin@vista-silicon.com + * + * Based on mxc-pcm.c by Liam Girdwood, (C) 2006 Wolfson Microelectronics PLC. + * and on mxc-alsa-mc13783 (C) 2006 Freescale. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <asm/dma.h> +#include <asm/hardware.h> + +#include "imx27-pcm.h" + +/* Just for debugging */ +#include "imx27-ssi.h" + +/* Taken from drivers/mxc/ssi/registers.h just provisionally */ +#define MXC_SSI1STX0 0x00 +#define MXC_SSI1SRX0 0x08 +#define MXC_SSI2STX0 0x00 +#define MXC_SSI2SRX0 0x08 + +static const struct snd_pcm_hardware mx27_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 mx27_runtime_data { + int dma_ch_tx; //DMA channel number for tx + int dma_ch_rx; //DMA channel number for rx + int active; //Is the stream active? + unsigned int period; //Period number + unsigned int periods; //¿? + int tx_spin; //¿? + spinlock_t dma_lock; //DMA spinlock + struct mx27_pcm_dma_param *dma_params; //FIXME this is SDMA api +}; + +/** + * 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 mx27_runtime_data *prtd = runtime->private_data; + unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size); + unsigned int offset = dma_size * prtd->periods; + unsigned long flags; + + spin_lock_irqsave(&prtd->dma_lock, flags); + + pr_debug("MX27 : audio_stop_dma active = 0\n"); + + prtd->active = 0; + prtd->period = 0; + prtd->periods = 0; + + /* this stops the dma channel and clears the buffer ptrs */ + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mxc_dma_disable(prtd->dma_ch_tx) < 0) + printk(KERN_ERR "audio_stop_dma: error when stopping dma transfer in channel %d \n", + prtd->dma_ch_tx); + } else { + if (mxc_dma_disable(prtd->dma_ch_rx) < 0) + printk(KERN_ERR "audio_stop_dma: error when stopping dma transfer in channel %d \n", + prtd->dma_ch_rx); + } + 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 mx27_runtime_data *prtd = runtime->private_data; + unsigned int dma_size; + unsigned int offset; + int ret = 0; + mxc_dma_requestbuf_t dma_request; + + + if (prtd->active) { + memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));; + dma_size = frames_to_bytes(runtime, runtime->period_size); + + pr_debug("dma_new_period: prtd->period (%x) runtime->periods (%d)\n", + prtd->period, runtime->periods); + pr_debug("dma_new_period: runtime->period_size (%d) dma_size (%d)\n", + (unsigned int)runtime->period_size, + runtime->dma_bytes); + + offset = dma_size * prtd->period; + snd_assert(dma_size <= mx27_pcm_hardware.period_bytes_max); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("\ndma_new_period (playback): runtime->dma_area = %x\noffset = %x\ndma_size = %x\n", + runtime->dma_area, offset, dma_size); + pr_debug("\ndma_new_period : dma_addr is %x\n", + (runtime->dma_addr + offset)); + + dma_request.src_addr = (dma_addr_t)(runtime->dma_addr + offset); +#ifdef CONFIG_SND_SOC_MX27_SSI1 + + dma_request.dst_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1STX0); + + pr_debug("dst_addr is %x\n", dma_request.dst_addr); + pr_debug("src_addr is %x\n", dma_request.src_addr); +#else + dma_request.dst_addr = + (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2STX0); +#endif + } else { + pr_debug("\ndma_new_period (capture): runtime->dma_area = %x\noffset = %x\ndma_size = %x\n", + runtime->dma_area, offset, dma_size); + pr_debug("\ndma_new_period: dma_addr is %x\n", + (runtime->dma_addr + offset)); + + dma_request.dst_addr = (dma_addr_t) (runtime->dma_addr + offset); +#ifdef CONFIG_SND_SOC_MX27_SSI1 + dma_request.src_addr = + (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1SRX0); +#else + dma_request.src_addr = + (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2SRX0); +#endif + } + + dma_request.num_of_bytes = dma_size; + pr_debug("dma_new_period: Start DMA offset (%d) size (%d)\n", + offset, runtime->dma_bytes); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("dma_new_period: configuring tx dma\n"); + ret = mxc_dma_config(prtd->dma_ch_tx, &dma_request, 1, + MXC_DMA_MODE_WRITE); + if (ret < 0) { + printk("dma_new_period: buffer could not be added for playback transfer\n"); + return ret; + } + + pr_debug("dma_new_period: enable tx dma transfer\n"); + ret = mxc_dma_enable(prtd->dma_ch_tx); + if (ret < 0) { + printk("dma_new_period: cannot queue DMA buffer\ + (%i)\n", ret); + return ret; + } + } else { + ret = mxc_dma_config(prtd->dma_ch_rx, &dma_request, 1, MXC_DMA_MODE_READ); + if (ret < 0) { + printk("dma_new_period: buffer could not be added for capture transfer\n"); + return ret; + } + + pr_debug("dma_new_period: enable dma transfer\n"); + ret = mxc_dma_enable(prtd->dma_ch_rx); + if(ret<0) { + printk("dma_new_period: cannot queue DMA buffer\ + (%i)\n", ret); + return ret; + } + } + + pr_debug("dma_new_period: transfer enabled\n src_addr = %x\n dst_addr = %x\n num_of_byes = %d\n", (void *) dma_request.src_addr, (void *) dma_request.dst_addr, dma_request.num_of_bytes); + + 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(void *data, int error, unsigned int count) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct mx27_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("audio_dma_irq: irq per %d offset %x\n", 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_mxc_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mx27_runtime_data *prtd = runtime->private_data; + + prtd->period = 0; + prtd->periods = 0; + + return 0; +} + +static int mxc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mx27_runtime_data *prtd = runtime->private_data; + int ret; + + if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + { + printk("imx27-pcm: failed to malloc pcm pages\n"); + return ret; + } + + pr_debug("mxc_pcm_hw_params: snd_pcm_lib_malloc pages has return OK\n"); + pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_addr 0x(%x)\n", + (unsigned int)runtime->dma_addr); + pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_area 0x(%x)\n", + (unsigned int)runtime->dma_area); + pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_bytes 0x(%x)\n", + (unsigned int)runtime->dma_bytes); + + return ret; +} + +static int mxc_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mx27_runtime_data *prtd = runtime->private_data; + + + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mxc_dma_free(prtd->dma_ch_tx); + else + mxc_dma_free(prtd->dma_ch_rx); + + snd_pcm_lib_free_pages(substream); + + return 0; +} + +static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct mx27_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("mxc_pcm_trigger: starting dma_new_period\n"); + ret = dma_new_period(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + /* requested stream shutdown */ + pr_debug("mxc_pcm_trigger: stopping dma transfer\n"); + ret = audio_stop_dma(substream); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mx27_runtime_data *prtd = runtime->private_data; + unsigned int offset = 0; + + pr_debug("mxc_pcm_pointer:\n runtime->period_size=%d\n prtd->periods=%d\n runtime->buffer_size=%d\n prtd->tx_spin=%d\n", + runtime->period_size, prtd->periods, runtime->buffer_size, prtd->tx_spin); + /* 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("mxc_pcm_pointer: pointer offset %x\n", offset); + + return offset; +} + +//Alocation of substream->runtime->private_data +static int mxc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mx27_runtime_data *prtd; + int ret; + + snd_soc_set_runtime_hwparams(substream, &mx27_pcm_hardware); + + if ((ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return ret; + + if ((prtd = kzalloc(sizeof(struct mx27_runtime_data), GFP_KERNEL)) == NULL) { + ret = -ENOMEM; + goto out; + } + + runtime->private_data = prtd; + + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + { + +#ifdef CONFIG_SND_SOC_MX27_SSI1 + pr_debug("mxc_pcm_open: Requesting MXC_DMA_SSI1_16BIT_TX0 dma channel\n"); + prtd->dma_ch_tx = mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA"); +#else + prtd->dma_ch_tx = mxc_dma_request(MXC_DMA_SSI2_16BIT_TX0, "ALSA TX DMA"); +#endif + if(prtd->dma_ch_tx < 0) + { + printk("error requesting a write(dma to device) dma channel\n"); + return ret; + } + + /* Set dma callback */ + pr_debug("mxc_pcm_open: Setting tx dma callback function\n"); + ret = mxc_dma_callback_set(prtd->dma_ch_tx, (mxc_dma_callback_t) audio_dma_irq, (void *)substream); + if(ret < 0) { + printk("error setting dma callback function\n"); + return ret; + } + + return 0; + } else { +#ifdef CONFIG_SND_SOC_MX27_SSI1 + pr_debug("mxc_pcm_open: Requesting MXC_DMA_SSI1_16BIT_RX0 dma channel\n"); + prtd->dma_ch_rx = mxc_dma_request(MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA"); +#else + prtd->dma_ch_rx = mxc_dma_request(MXC_DMA_SSI2_16BIT_RX0, "ALSA RX DMA"); +#endif + if (prtd->dma_ch_rx < 0) { + printk("error requesting a read(dma from device) dma channel\n"); + return ret; + } + + //Set dma callback + pr_debug("mxc_pcm_open: Setting rx dma callback function\n"); + if (mxc_dma_callback_set(prtd->dma_ch_rx, (mxc_dma_callback_t) audio_dma_irq, (void *)substream)<0) + printk("error setting dma callback function\n"); + + return 0; + } + + + + out: + return ret; +} + +static int mxc_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxc_runtime_data *prtd = runtime->private_data; + + kfree(prtd); + + return 0; +} + +static int +mxc_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); +} + +struct snd_pcm_ops mxc_pcm_ops = { + .open = mxc_pcm_open, + .close = mxc_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = mxc_pcm_hw_params, + .hw_free = mxc_pcm_hw_free, + .prepare = snd_mxc_prepare, + .trigger = mxc_pcm_trigger, + .pointer = mxc_pcm_pointer, + .mmap = mxc_pcm_mmap, +}; + +static u64 mxc_pcm_dmamask = 0xffffffff; + +static int imx31_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 = mx27_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("imx27_pcm: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", (void *) buf->area, (void *) buf->addr, size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static void imx31_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; + } +} + +int mxc_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 = &mxc_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = imx31_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + printk("mxc_pcm_new: preallocate playback buffer\n"); + if (ret) + goto out; + } + + if (dai->capture.channels_min) { + ret = imx31_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + printk("mxc_pcm_new: preallocate capture buffer\n"); + if (ret) + goto out; + } + out: + return ret; +} + +struct snd_soc_platform imx27_soc_platform = { + .name = "mxc-audio", + .pcm_ops = &mxc_pcm_ops, + .pcm_new = mxc_pcm_new, + .pcm_free = imx31_pcm_free_dma_buffers, +}; + +EXPORT_SYMBOL_GPL(imx27_soc_platform); + +MODULE_AUTHOR("Javier Martin"); +MODULE_DESCRIPTION("Freescale i.MX27 PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/imx27-pcm.h b/sound/soc/imx/imx27-pcm.h new file mode 100644 index 0000000..9852cff --- /dev/null +++ b/sound/soc/imx/imx27-pcm.h @@ -0,0 +1,97 @@ +/* + * mxc-pcm.h :- ASoC platform header for Freescale i.MX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MXC_PCM_H +#define _MXC_PCM_H + +#include <asm/arch/dma.h> + +/* AUDMUX regs definition - MOVE to asm/arch when stable +#define AUDMUX_IO_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR) + +#define DAM_PTCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x00))) +#define DAM_PDCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x04))) +#define DAM_PTCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x08))) +#define DAM_PDCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x0C))) +#define DAM_PTCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x10))) +#define DAM_PDCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x14))) +#define DAM_PTCR4 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x18))) +#define DAM_PDCR4 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x1C))) +#define DAM_PTCR5 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x20))) +#define DAM_PDCR5 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x24))) +#define DAM_PTCR6 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x28))) +#define DAM_PDCR6 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x2C))) +#define DAM_PTCR7 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x30))) +#define DAM_PDCR7 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x34))) +#define DAM_CNMCR (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x38))) +#define DAM_PTCR(a) (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + a*8))) +#define DAM_PDCR(a) (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 4 + a*8))) + + +#define AUDMUX_PTCR_TFSDIR (1 << 31) +#define AUDMUX_PTCR_TFSSEL(x, y) ((x << 30) | (((y - 1) & 0x7) << 27)) +#define AUDMUX_PTCR_TCLKDIR (1 << 26) +#define AUDMUX_PTCR_TCSEL(x, y) ((x << 25) | (((y - 1) & 0x7) << 22)) +#define AUDMUX_PTCR_RFSDIR (1 << 21) +#define AUDMUX_PTCR_RFSSEL(x, y) ((x << 20) | (((y - 1) & 0x7) << 17)) +#define AUDMUX_PTCR_RCLKDIR (1 << 16) +#define AUDMUX_PTCR_RCSEL(x, y) ((x << 15) | (((y - 1) & 0x7) << 12)) +#define AUDMUX_PTCR_SYN (1 << 11) + +#define AUDMUX_FROM_TXFS 0 +#define AUDMUX_FROM_RXFS 1 + +#define AUDMUX_PDCR_RXDSEL(x) (((x - 1) & 0x7) << 13) +#define AUDMUX_PDCR_TXDXEN (1 << 12) +#define AUDMUX_PDCR_MODE(x) (((x) & 0x3) << 8) +#define AUDMUX_PDCR_INNMASK(x) (((x) & 0xff) << 0) + +#define AUDMUX_CNMCR_CEN (1 << 18) +#define AUDMUX_CNMCR_FSPOL (1 << 17) +#define AUDMUX_CNMCR_CLKPOL (1 << 16) +#define AUDMUX_CNMCR_CNTHI(x) (((x) & 0xff) << 8) +#define AUDMUX_CNMCR_CNTLOW(x) (((x) & 0xff) << 0) +*/ + +/* Previous defines are only valid for imx31 */ +#define AUDMUX_IO_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR) + +#define DAM_HPCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x00))) +#define DAM_HPCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x04))) +#define DAM_HPCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x08))) +#define DAM_PPCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x10))) +#define DAM_PPCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x14))) +#define DAM_PPCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x1C))) + +#define AUDMUX_HPCR_TFSDIR (1 << 31) +#define AUDMUX_HPCR_TCLKDIR (1 << 30) +#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0xff) << 26) +#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13) +#define AUDMUX_HPCR_SYN (1 << 12) + +#define AUDMUX_PPCR_TFSDIR (1 << 31) +#define AUDMUX_PPCR_TCLKDIR (1 << 30) +#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0xff) << 26) +#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13) +#define AUDMUX_PPCR_SYN (1 << 12) + + + +struct mx27_pcm_dma_params { + char *name; /* stream identifier */ + mxc_dma_requestbuf_t buf_params; /*DMA buffer params */ + +}; + +extern struct snd_soc_dai mxc_ssi_dai[3]; + +/* platform data */ +extern struct snd_soc_platform imx27_soc_platform; + + +#endif