diff options
author | Lily Zhang <r58066@freescale.com> | 2010-09-13 21:18:16 +0800 |
---|---|---|
committer | Alan Tull <r80115@freescale.com> | 2010-09-25 09:49:26 -0500 |
commit | 829f1baab95b759dfdeb1f2e015e434252b1b292 (patch) | |
tree | ec654b4d54fee8b1f4bc2f5496a596bc8b5c7eec /sound/soc | |
parent | 57bdba19407c73db152d3cf06c152c9a857f63e1 (diff) |
ENGR00131389-2 switch ssi to fsl ssi driver
1. Switch ssi to fsl ssi driver instead of kernel upstream driver
2. Fix build break
3. remove "area->vm_ops->open" calling in imx-pcm.c to support IRAM
playback
Signed-off-by: Lily Zhang <r58066@freescale.com>
Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/imx/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/imx/Makefile | 5 | ||||
-rw-r--r-- | sound/soc/imx/imx-3stack-sgtl5000.c | 5 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm.c | 3 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.c | 1097 | ||||
-rw-r--r-- | sound/soc/imx/imx-ssi.h | 441 |
6 files changed, 792 insertions, 769 deletions
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 7264bd50401e..38b7dcd49a35 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -1,15 +1,15 @@ -config SND_IMX_SOC +config SND_MXC_SOC tristate "SoC Audio for Freescale i.MX CPUs" depends on ARCH_MXC select SND_PCM - select FIQ - select SND_SOC_AC97_BUS help Say Y or M if you want to add support for codecs attached to the i.MX SSI interface. +if SND_MXC_SOC + config SND_MXC_SOC_SSI - tristate + tristate config SND_MXC_SOC_WM1133_EV1 tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" @@ -35,8 +35,6 @@ config SND_MXC_SOC_ESAI config SND_MXC_SOC_AC97 tristate -if SND_MXC_SOC - config SND_MXC_SOC_IRAM bool "Locate Audio DMA playback buffers in IRAM" help diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 0a63a2fa93ab..657777f6dd2f 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -1,13 +1,12 @@ # i.MX Platform Support -snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o imx-pcm.o +snd-soc-imx-objs := imx-pcm.o +snd-soc-imx-ssi-objs := imx-ssi.o snd-soc-imx-esai-objs := imx-esai.o ifdef CONFIG_MACH_MX27 snd-soc-imx-objs += imx-pcm-dma-mx2.o endif -obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o - # i.MX Machine Support snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o diff --git a/sound/soc/imx/imx-3stack-sgtl5000.c b/sound/soc/imx/imx-3stack-sgtl5000.c index 16530d01904c..e73ec62f3251 100644 --- a/sound/soc/imx/imx-3stack-sgtl5000.c +++ b/sound/soc/imx/imx-3stack-sgtl5000.c @@ -24,6 +24,7 @@ #include <linux/err.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/fsl_devices.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -153,7 +154,9 @@ static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream, /* set i.MX active slot mask */ snd_soc_dai_set_tdm_slot(cpu_dai, - channels == 1 ? 0xfffffffe : 0xfffffffc, 2); + channels == 1 ? 0xfffffffe : 0xfffffffc, + channels == 1 ? 0xfffffffe : 0xfffffffc, + 2, 32); /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c index 3e8a90da35c2..348fbaea672f 100644 --- a/sound/soc/imx/imx-pcm.c +++ b/sound/soc/imx/imx-pcm.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/iram_alloc.h> +#include <linux/fsl_devices.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -106,8 +107,6 @@ static int imx_iram_audio_playback_mmap(struct snd_pcm_substream *substream, ret = remap_pfn_range(area, area->vm_start, phys >> PAGE_SHIFT, size, area->vm_page_prot); - if (ret == 0) - area->vm_ops->open(area); return ret; } diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index e617f89b6eb4..20e29c12f54f 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -1,83 +1,227 @@ /* - * imx-ssi.c -- ALSA Soc Audio Layer + * imx-ssi.c -- SSI driver for Freescale IMX * - * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. + * Copyright (C) 2006-2010 Freescale Semiconductor, Inc. + * Based on mxc-alsa-mc13783 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * + * Revision history + * 29th Aug 2006 Initial version. + * + * TODO: + * Need to rework SSI register defs when new defs go into mainline. + * Add support for TDM and FIFO 1. * - * The i.MX SSI core has some nasty limitations in AC97 mode. While most - * sane processor vendors have a FIFO per AC97 slot, the i.MX has only - * one FIFO which combines all valid receive slots. We cannot even select - * which slots we want to receive. The WM9712 with which this driver - * was developped with always sends GPIO status data in slot 12 which - * we receive in our (PCM-) data stream. The only chance we have is to - * manually skip this data in the FIQ handler. With sampling rates different - * from 48000Hz not every frame has valid receive data, so the ratio - * between pcm data and GPIO status data changes. Our FIQ handler is not - * able to handle this, hence this driver only works with 48000Hz sampling - * rate. - * Reading and writing AC97 registers is another challange. The core - * provides us status bits when the read register is updated with *another* - * value. When we read the same register two times (and the register still - * contains the same value) these status bits are not set. We work - * around this by not polling these bits but only wait a fixed delay. - * */ -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> - +#include <linux/dma-mapping.h> +#include <linux/clk.h> #include <sound/core.h> -#include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> - #include <mach/dma.h> -#include <mach/ssi.h> +#include <mach/clock.h> +#include <asm/mach-types.h> #include <mach/hardware.h> #include "imx-ssi.h" #include "imx-pcm.h" -#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) +#define SSI_STX0 (0x00) +#define SSI_STX1 (0x04) +#define SSI_SRX0 (0x08) +#define SSI_SRX1 (0x0c) +#define SSI_SCR (0x10) +#define SSI_SISR (0x14) +#define SSI_SIER (0x18) +#define SSI_STCR (0x1c) +#define SSI_SRCR (0x20) +#define SSI_STCCR (0x24) +#define SSI_SRCCR (0x28) +#define SSI_SFCSR (0x2c) +#define SSI_STR (0x30) +#define SSI_SOR (0x34) +#define SSI_SACNT (0x38) +#define SSI_SACADD (0x3c) +#define SSI_SACDAT (0x40) +#define SSI_SATAG (0x44) +#define SSI_STMSK (0x48) +#define SSI_SRMSK (0x4c) +#define SSI_SACCST (0x50) +#define SSI_SACCEN (0x54) +#define SSI_SACCDIS (0x58) + +/* debug */ +#define IMX_SSI_DEBUG 0 +#if IMX_SSI_DEBUG +#define dbg(format, arg...) printk(format, ## arg) +#else +#define dbg(format, arg...) +#endif + +#define IMX_SSI_DUMP 0 +#if IMX_SSI_DUMP +#define SSI_DUMP() \ + do { \ + printk(KERN_INFO "dump @ %s\n", __func__); \ + printk(KERN_INFO "scr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SCR), \ + __raw_readl(ioaddr + SSI_SCR)); \ + printk(KERN_INFO "sisr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SISR), \ + __raw_readl(ioaddr + SSI_SISR)); \ + printk(KERN_INFO "stcr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_STCR), \ + __raw_readl(ioaddr + SSI_STCR)); \ + printk(KERN_INFO "srcr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SRCR), \ + __raw_readl(ioaddr + SSI_SRCR)); \ + printk(KERN_INFO "stccr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_STCCR), \ + __raw_readl(ioaddr + SSI_STCCR)); \ + printk(KERN_INFO "srccr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SRCCR), \ + __raw_readl(ioaddr + SSI_SRCCR)); \ + printk(KERN_INFO "sfcsr %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SFCSR), \ + __raw_readl(ioaddr + SSI_SFCSR)); \ + printk(KERN_INFO "stmsk %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_STMSK), \ + __raw_readl(ioaddr + SSI_STMSK)); \ + printk(KERN_INFO "srmsk %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SRMSK), \ + __raw_readl(ioaddr + SSI_SRMSK)); \ + printk(KERN_INFO "sier %x\t, %x\n", \ + __raw_readl(ioaddr + SSI_SIER), \ + __raw_readl(ioaddr + SSI_SIER)); \ + } while (0); +#else +#define SSI_DUMP() +#endif + +/* + * SSI system clock configuration. + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 scr; + + scr = __raw_readl(ioaddr + SSI_SCR); + + if (scr & SSI_SCR_SSIEN) + return 0; + + switch (clk_id) { + case IMX_SSP_SYS_CLK: + if (dir == SND_SOC_CLOCK_OUT) + scr |= SSI_SCR_SYS_CLK_EN; + else + scr &= ~SSI_SCR_SYS_CLK_EN; + break; + default: + return -EINVAL; + } + + __raw_writel(scr, ioaddr + SSI_SCR); + + return 0; +} + +/* + * SSI Clock dividers + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 stccr, srccr; + + if (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) + return 0; + + srccr = __raw_readl(ioaddr + SSI_SRCCR); + stccr = __raw_readl(ioaddr + SSI_STCCR); + + switch (div_id) { + case IMX_SSI_TX_DIV_2: + stccr &= ~SSI_STCCR_DIV2; + stccr |= div; + break; + case IMX_SSI_TX_DIV_PSR: + stccr &= ~SSI_STCCR_PSR; + stccr |= div; + break; + case IMX_SSI_TX_DIV_PM: + stccr &= ~0xff; + stccr |= SSI_STCCR_PM(div); + break; + case IMX_SSI_RX_DIV_2: + stccr &= ~SSI_STCCR_DIV2; + stccr |= div; + break; + case IMX_SSI_RX_DIV_PSR: + stccr &= ~SSI_STCCR_PSR; + stccr |= div; + break; + case IMX_SSI_RX_DIV_PM: + stccr &= ~0xff; + stccr |= SSI_STCCR_PM(div); + break; + default: + return -EINVAL; + } + + __raw_writel(stccr, ioaddr + SSI_STCCR); + __raw_writel(srccr, ioaddr + SSI_SRCCR); + + return 0; +} /* * SSI Network Mode or TDM slots configuration. * Should only be called when port is inactive (i.e. SSIEN = 0). */ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, - unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) { - struct imx_ssi *ssi = cpu_dai->private_data; - u32 sccr; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 stmsk, srmsk, stccr; - sccr = readl(ssi->base + SSI_STCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_STCCR); + if (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) + return 0; + stccr = __raw_readl(ioaddr + SSI_STCCR); - sccr = readl(ssi->base + SSI_SRCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_SRCCR); + stmsk = tx_mask; + srmsk = rx_mask; + stccr &= ~SSI_STCCR_DC_MASK; + stccr |= SSI_STCCR_DC(slots - 1); - writel(tx_mask, ssi->base + SSI_STMSK); - writel(rx_mask, ssi->base + SSI_SRMSK); + __raw_writel(stmsk, ioaddr + SSI_STMSK); + __raw_writel(srmsk, ioaddr + SSI_SRMSK); + __raw_writel(stccr, ioaddr + SSI_STCCR); + __raw_writel(stccr, ioaddr + SSI_SRCCR); return 0; } @@ -86,144 +230,225 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, * SSI DAI format configuration. * Should only be called when port is inactive (i.e. SSIEN = 0). * Note: We don't use the I2S modes but instead manually configure the - * SSI for I2S because the I2S mode is only a register preset. + * SSI for I2S. */ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct imx_ssi *ssi = cpu_dai->private_data; - u32 strcr = 0, scr; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 stcr = 0, srcr = 0, scr; + + scr = __raw_readl(ioaddr + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); - scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); + if (scr & SSI_SCR_SSIEN) + return 0; /* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* data on rising edge of bclk, frame low 1clk before data */ - strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; - scr |= SSI_SCR_NET; + stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; + srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_LEFT_J: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TXBIT0; + stcr |= SSI_STCR_TXBIT0; + srcr |= SSI_SRCR_RXBIT0; break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL; + stcr |= SSI_STCR_TFSL; + srcr |= SSI_SRCR_RFSL; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; + stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS; + srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS; break; } /* DAI clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: - strcr |= SSI_STCR_TFSI; - strcr &= ~SSI_STCR_TSCKP; + stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); + srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI); break; case SND_SOC_DAIFMT_IB_NF: - strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); + stcr |= SSI_STCR_TFSI; + stcr &= ~SSI_STCR_TSCKP; + srcr |= SSI_SRCR_RFSI; + srcr &= ~SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_IF: - strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; + stcr &= ~SSI_STCR_TFSI; + stcr |= SSI_STCR_TSCKP; + srcr &= ~SSI_SRCR_RFSI; + srcr |= SSI_SRCR_RSCKP; break; case SND_SOC_DAIFMT_NB_NF: - strcr &= ~SSI_STCR_TFSI; - strcr |= SSI_STCR_TSCKP; + stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; + srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP; break; } /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) + && priv->network_mode) { + scr &= ~SSI_SCR_I2S_MODE_MASK; + scr |= SSI_SCR_I2S_MODE_MSTR; + } + break; + case SND_SOC_DAIFMT_CBM_CFS: + stcr |= SSI_STCR_TFDIR; + srcr |= SSI_SRCR_RFDIR; + break; + case SND_SOC_DAIFMT_CBS_CFM: + stcr |= SSI_STCR_TXDIR; + srcr |= SSI_SRCR_RXDIR; + break; case SND_SOC_DAIFMT_CBM_CFM: + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) + && priv->network_mode) { + scr &= ~SSI_SCR_I2S_MODE_MASK; + scr |= SSI_SCR_I2S_MODE_SLAVE; + } break; - default: - /* Master mode not implemented, needs handling of clocks. */ - return -EINVAL; } - strcr |= SSI_STCR_TFEN0; + /* sync */ + if (priv->sync_mode) + scr |= SSI_SCR_SYN; + + /* tdm - only for stereo atm */ + if (priv->network_mode) + scr |= SSI_SCR_NET; - writel(strcr, ssi->base + SSI_STCR); - writel(strcr, ssi->base + SSI_SRCR); - writel(scr, ssi->base + SSI_SCR); + __raw_writel(stcr, ioaddr + SSI_STCR); + __raw_writel(srcr, ioaddr + SSI_SRCR); + __raw_writel(scr, ioaddr + SSI_SCR); + SSI_DUMP(); return 0; } -/* - * SSI system clock configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) +static int imx_ssi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { - struct imx_ssi *ssi = cpu_dai->private_data; - u32 scr; - - scr = readl(ssi->base + SSI_SCR); - - switch (clk_id) { - case IMX_SSP_SYS_CLK: - if (dir == SND_SOC_CLOCK_OUT) - scr |= SSI_SCR_SYS_CLK_EN; - else - scr &= ~SSI_SCR_SYS_CLK_EN; - break; - default: - return -EINVAL; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + + /* we cant really change any SSI values after SSI is enabled + * need to fix in software for max flexibility - lrg */ + if (cpu_dai->playback.active || cpu_dai->capture.active) + return 0; + + /* reset the SSI port - Sect 45.4.4 */ + if (clk_get_usecount(priv->ssi_clk) != 0) { + clk_enable(priv->ssi_clk); + return 0; } - writel(scr, ssi->base + SSI_SCR); + __raw_writel(0, ioaddr + SSI_SCR); + clk_enable(priv->ssi_clk); + + /* BIG FAT WARNING + * SDMA FIFO watermark must == SSI FIFO watermark for best results. + */ + __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) | + SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) | + SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) | + SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)), + ioaddr + SSI_SFCSR); + __raw_writel(0, ioaddr + SSI_SIER); + SSI_DUMP(); return 0; } -/* - * SSI Clock dividers - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) +static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) { - struct imx_ssi *ssi = cpu_dai->private_data; - u32 stccr, srccr; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 stccr, stcr, sier; - stccr = readl(ssi->base + SSI_STCCR); - srccr = readl(ssi->base + SSI_SRCCR); + stccr = __raw_readl(ioaddr + SSI_STCCR) & ~SSI_STCCR_WL_MASK; + stcr = __raw_readl(ioaddr + SSI_STCR); + sier = __raw_readl(ioaddr + SSI_SIER); - switch (div_id) { - case IMX_SSI_TX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; + /* DAI data (word) size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + stccr |= SSI_STCCR_WL(16); break; - case IMX_SSI_TX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; + case SNDRV_PCM_FORMAT_S20_3LE: + stccr |= SSI_STCCR_WL(20); break; - case IMX_SSI_TX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); + case SNDRV_PCM_FORMAT_S24_LE: + stccr |= SSI_STCCR_WL(24); break; - case IMX_SSI_RX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; + } + + /* enable interrupts */ + if ((cpu_dai->id & 0x1) == 0) + stcr |= SSI_STCR_TFEN0; + else + stcr |= SSI_STCR_TFEN1; + sier |= SSI_SIER_TDMAE | SSI_SIER_TIE | SSI_SIER_TUE0_EN; + + __raw_writel(stcr, ioaddr + SSI_STCR); + __raw_writel(stccr, ioaddr + SSI_STCCR); + __raw_writel(sier, ioaddr + SSI_SIER); + + return 0; +} + +static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + u32 srccr, srcr, sier; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + bool sync_mode = priv->sync_mode; + + srccr = + sync_mode ? __raw_readl(ioaddr + SSI_STCCR) : + __raw_readl(ioaddr + SSI_SRCCR); + srcr = __raw_readl(ioaddr + SSI_SRCR); + sier = __raw_readl(ioaddr + SSI_SIER); + srccr &= ~SSI_SRCCR_WL_MASK; + + /* DAI data (word) size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + srccr |= SSI_SRCCR_WL(16); break; - case IMX_SSI_RX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; + case SNDRV_PCM_FORMAT_S20_3LE: + srccr |= SSI_SRCCR_WL(20); break; - case IMX_SSI_RX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); + case SNDRV_PCM_FORMAT_S24_LE: + srccr |= SSI_SRCCR_WL(24); break; - default: - return -EINVAL; } - writel(stccr, ssi->base + SSI_STCCR); - writel(srccr, ssi->base + SSI_SRCCR); + /* enable interrupts */ + if ((cpu_dai->id & 0x1) == 0) + srcr |= SSI_SRCR_RFEN0; + else + srcr |= SSI_SRCR_RFEN1; + sier |= SSI_SIER_RDMAE | SSI_SIER_RIE | SSI_SIER_ROE0_EN; + + __raw_writel(srcr, ioaddr + SSI_SRCR); + if (sync_mode) + __raw_writel(srccr, ioaddr + SSI_STCCR); + else + __raw_writel(srccr, ioaddr + SSI_SRCCR); + __raw_writel(sier, ioaddr + SSI_SIER); return 0; } @@ -236,535 +461,327 @@ static int imx_ssi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct imx_ssi *ssi = cpu_dai->private_data; - struct imx_pcm_dma_params *dma_data; - u32 reg, sccr; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + int id; + + id = cpu_dai->id; /* Tx/Rx config */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - reg = SSI_STCCR; - dma_data = &ssi->dma_params_tx; + /* cant change any parameters when SSI is running */ + if ((__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) && + (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_TE)) + return 0; + return imx_ssi_hw_tx_params(substream, params, cpu_dai); } else { - reg = SSI_SRCCR; - dma_data = &ssi->dma_params_rx; + /* cant change any parameters when SSI is running */ + if ((__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_SSIEN) && + (__raw_readl(ioaddr + SSI_SCR) & SSI_SCR_RE)) + return 0; + return imx_ssi_hw_rx_params(substream, params, cpu_dai); } +} - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - - sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; - - /* DAI data (word) size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - sccr |= SSI_SRCCR_WL(16); - break; - case SNDRV_PCM_FORMAT_S20_3LE: - sccr |= SSI_SRCCR_WL(20); - break; - case SNDRV_PCM_FORMAT_S24_LE: - sccr |= SSI_SRCCR_WL(24); - break; - } +static int imx_ssi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 scr; - writel(sccr, ssi->base + reg); + /* enable the SSI port, note that no other port config + * should happen after SSIEN is set */ + scr = __raw_readl(ioaddr + SSI_SCR); + __raw_writel((scr | SSI_SCR_SSIEN), ioaddr + SSI_SCR); + SSI_DUMP(); return 0; } static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - struct imx_ssi *ssi = cpu_dai->private_data; - unsigned int sier_bits, sier; - unsigned int scr; + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + u32 scr; - scr = readl(ssi->base + SSI_SCR); - sier = readl(ssi->base + SSI_SIER); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_TDMAE; - else - sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; - } else { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_RDMAE; - else - sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; - } + scr = __raw_readl(ioaddr + SSI_SCR); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (scr & SSI_SCR_RE) { + __raw_writel(0, ioaddr + SSI_SCR); + } scr |= SSI_SCR_TE; - else + } else scr |= SSI_SCR_RE; - sier |= sier_bits; - - if (++ssi->enabled == 1) - scr |= SSI_SCR_SSIEN; - break; - - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) scr &= ~SSI_SCR_TE; else scr &= ~SSI_SCR_RE; - sier &= ~sier_bits; - - if (--ssi->enabled == 0) - scr &= ~SSI_SCR_SSIEN; - break; default: return -EINVAL; } + __raw_writel(scr, ioaddr + SSI_SCR); - if (!(ssi->flags & IMX_SSI_USE_AC97)) - /* rx/tx are always enabled to access ac97 registers */ - writel(scr, ssi->base + SSI_SCR); - - writel(sier, ssi->base + SSI_SIER); - + SSI_DUMP(); return 0; } -static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { - .startup = imx_ssi_startup, - .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, -}; - -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 | \ - SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE), - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE), - }, - .ops = &imx_ssi_pcm_dai_ops, -}; - -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = dma_mmap_coherent(NULL, vma, runtime->dma_area, - runtime->dma_addr, runtime->dma_bytes); - - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); - return ret; -} - -static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static void imx_ssi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = IMX_SSI_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - - return 0; -} - -static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); + struct imx_ssi *priv = (struct imx_ssi *)cpu_dai->private_data; + void __iomem *ioaddr = priv->ioaddr; + int id; -int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) -{ + id = cpu_dai->id; - 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; - } + /* shutdown SSI if neither Tx or Rx is active */ + if (cpu_dai->playback.active || cpu_dai->capture.active) + return; - if (dai->capture.channels_min) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; + if (clk_get_usecount(priv->ssi_clk) > 1) { + clk_disable(priv->ssi_clk); + return; } -out: - return ret; -} + __raw_writel(0, ioaddr + SSI_SCR); -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; - } + clk_disable(priv->ssi_clk); } -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) +#ifdef CONFIG_PM +static int imx_ssi_suspend(struct snd_soc_dai *dai) { - 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); + if (!dai->active) + return 0; - writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | - SSI_SCR_TE | SSI_SCR_RE, - base + SSI_SCR); + /* do we need to disable any clocks? */ - writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); - writel(0xff, base + SSI_SACCDIS); - writel(0x300, base + SSI_SACCEN); + return 0; } -static struct imx_ssi *ac97_ssi; - -static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) +static int imx_ssi_resume(struct snd_soc_dai *dai) { - struct imx_ssi *imx_ssi = ac97_ssi; - void __iomem *base = imx_ssi->base; - unsigned int lreg; - unsigned int lval; + if (!dai->active) + return 0; - if (reg > 0x7f) - return; + /* do we need to enable any clocks? */ - pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); - - lreg = reg << 12; - writel(lreg, base + SSI_SACADD); + return 0; +} +#else +#define imx_ssi_suspend NULL +#define imx_ssi_resume NULL +#endif - lval = val << 4; - writel(lval , base + SSI_SACDAT); +static int fifo_err_counter; - writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); - udelay(100); +static irqreturn_t imx_ssi_irq(int irq, void *dev_id) +{ + struct imx_ssi *priv = (struct imx_ssi *)dev_id; + void __iomem *ioaddr = priv->ioaddr; + if (fifo_err_counter++ % 1000 == 0) + printk(KERN_ERR "%s %s SISR %x SIER %x fifo_errs=%d\n", + __func__, priv->pdev->name, + __raw_readl(ioaddr + SSI_SISR), + __raw_readl(ioaddr + SSI_SIER), fifo_err_counter); + __raw_writel((SSI_SIER_TUE0_EN | SSI_SIER_ROE0_EN), ioaddr + SSI_SISR); + return IRQ_HANDLED; } -static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) +static int imx_ssi_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { - 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); + struct imx_ssi *priv = (struct imx_ssi *)dai->private_data; + + if (priv->irq >= 0) { + if (request_irq(priv->irq, imx_ssi_irq, IRQF_SHARED, + pdev->name, priv)) { + printk(KERN_ERR "%s: failure requesting irq for %s\n", + __func__, pdev->name); + return -EBUSY; + } + } - return val; + return 0; } -static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) +static void imx_ssi_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) { - struct imx_ssi *imx_ssi = ac97_ssi; + struct imx_ssi *priv = (struct imx_ssi *)dai->private_data; - if (imx_ssi->ac97_reset) - imx_ssi->ac97_reset(ac97); + if (priv->irq >= 0) + free_irq(priv->irq, dai); } -static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) -{ - struct imx_ssi *imx_ssi = ac97_ssi; +#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) - if (imx_ssi->ac97_warm_reset) - imx_ssi->ac97_warm_reset(ac97); -} +#define IMX_SSI_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) -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 +static struct snd_soc_dai_ops imx_ssi_dai_ops = { + .startup = imx_ssi_startup, + .shutdown = imx_ssi_shutdown, + .trigger = imx_ssi_trigger, + .prepare = imx_ssi_prepare, + .hw_params = imx_ssi_hw_params, + .set_sysclk = imx_ssi_set_dai_sysclk, + .set_clkdiv = imx_ssi_set_dai_clkdiv, + .set_fmt = imx_ssi_set_dai_fmt, + .set_tdm_slot = imx_ssi_set_dai_tdm_slot, }; -EXPORT_SYMBOL_GPL(soc_ac97_ops); -struct snd_soc_dai *imx_ssi_pcm_dai[MAX_SSI_CHANNELS]; -EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai); +struct snd_soc_dai *imx_ssi_dai[MAX_SSI_CHANNELS]; +EXPORT_SYMBOL_GPL(imx_ssi_dai); -static int imx_ssi_probe(struct platform_device *pdev) +static int imx_ssi_dev_probe(struct platform_device *pdev) { + int fifo0_channel = pdev->id * 2; + struct snd_soc_dai *dai; + struct imx_ssi *priv; + int fifo, channel; struct resource *res; - struct imx_ssi *ssi; - struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; - struct snd_soc_platform *platform; - int ret = 0; - unsigned int val; - struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id]; - - if (dai->id >= ARRAY_SIZE(imx_ssi_pcm_dai)) - return -EINVAL; + int ret; - ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); - if (!ssi) + BUG_ON(fifo0_channel >= MAX_SSI_CHANNELS); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = kzalloc(sizeof(struct imx_ssi), GFP_KERNEL); + if (!priv) return -ENOMEM; - if (pdata) { - ssi->ac97_reset = pdata->ac97_reset; - ssi->ac97_warm_reset = pdata->ac97_warm_reset; - ssi->flags = pdata->flags; - } + /* Each SSI block has 2 fifos which share the same + private data (struct imx_ssi) */ + priv->baseaddr = res->start; + priv->ioaddr = ioremap(res->start, 0x5C); + priv->irq = platform_get_irq(pdev, 0); + priv->ssi_clk = clk_get(&pdev->dev, "ssi_clk"); + priv->pdev = pdev; + + for (fifo = 0; fifo < 2; fifo++) { + channel = (pdev->id * 2) + fifo; + + dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); + if (IS_ERR(dai)) { + ret = -ENOMEM; + goto DAI_ERR; + } - ssi->irq = platform_get_irq(pdev, 0); + dai->name = kasprintf(GFP_KERNEL, "imx-ssi-%d-%d", + pdev->id + 1, fifo); + if (IS_ERR(dai->name)) { + kfree(dai); + ret = -ENOMEM; + goto DAI_ERR; + } - ssi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ssi->clk)) { - ret = PTR_ERR(ssi->clk); - dev_err(&pdev->dev, "Cannot get the clock: %d\n", - ret); - goto failed_clk; - } - clk_enable(ssi->clk); + dai->probe = imx_ssi_probe; + dai->suspend = imx_ssi_suspend; + dai->remove = imx_ssi_remove; + dai->resume = imx_ssi_resume; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto failed_get_resource; - } + dai->playback.channels_min = 1; + dai->playback.channels_max = 2; + dai->playback.rates = IMX_SSI_RATES; + dai->playback.formats = IMX_SSI_FORMATS; - if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - ret = -EBUSY; - goto failed_get_resource; - } + dai->capture.channels_min = 1; + dai->capture.channels_max = 2; + dai->capture.rates = IMX_SSI_RATES; + dai->capture.formats = IMX_SSI_FORMATS; - ssi->base = ioremap(res->start, resource_size(res)); - if (!ssi->base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENODEV; - goto failed_ioremap; - } + dai->ops = &imx_ssi_dai_ops; - if (ssi->flags & IMX_SSI_USE_AC97) { - if (ac97_ssi) { - ret = -EBUSY; - goto failed_ac97; - } - ac97_ssi = ssi; - setup_channel_to_ac97(ssi); - memcpy(dai, &imx_ac97_dai, sizeof(imx_ac97_dai)); - } else - memcpy(dai, &imx_ssi_dai, sizeof(imx_ssi_dai)); - - writel(0x0, ssi->base + SSI_SIER); - - ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; - ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); - if (res) - ssi->dma_params_tx.dma = res->start; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); - if (res) - ssi->dma_params_rx.dma = res->start; - - dai->id = pdev->id; - dai->dev = &pdev->dev; - dai->name = kasprintf(GFP_KERNEL, "imx-ssi.%d", pdev->id); - dai->private_data = ssi; - - if ((cpu_is_mx27() || cpu_is_mx21()) && - !(ssi->flags & IMX_SSI_USE_AC97) && - (ssi->flags & IMX_SSI_DMA)) { - ssi->flags |= IMX_SSI_DMA; - platform = imx_ssi_dma_mx2_init(pdev, ssi); - } else - platform = imx_ssi_fiq_init(pdev, ssi); - - imx_soc_platform.pcm_ops = platform->pcm_ops; - imx_soc_platform.pcm_new = platform->pcm_new; - imx_soc_platform.pcm_free = platform->pcm_free; - - val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) | - SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize); - writel(val, ssi->base + SSI_SFCSR); - - ret = snd_soc_register_dai(dai); - if (ret) { - dev_err(&pdev->dev, "register DAI failed\n"); - goto failed_register; - } + dai->private_data = priv; - platform_set_drvdata(pdev, ssi); + dai->id = channel; + imx_ssi_dai[channel] = dai; + ret = snd_soc_register_dai(dai); + if (ret < 0) { + kfree(dai->name); + kfree(dai); + goto DAI_ERR; + } + } return 0; -failed_register: -failed_ac97: - iounmap(ssi->base); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_get_resource: - clk_disable(ssi->clk); - clk_put(ssi->clk); -failed_clk: - kfree(ssi); +DAI_ERR: + if (fifo == 1) { + dai = imx_ssi_dai[fifo0_channel]; + snd_soc_unregister_dai(dai); + kfree(dai->name); + kfree(dai); + } + clk_put(priv->ssi_clk); + iounmap(priv->ioaddr); + kfree(priv); return ret; } -static int __devexit imx_ssi_remove(struct platform_device *pdev) +static int __devexit imx_ssi_dev_remove(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct imx_ssi *ssi = platform_get_drvdata(pdev); - struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id]; + int fifo0_channel = pdev->id * 2; + int fifo1_channel = (pdev->id * 2) + 1; + struct imx_ssi *priv = imx_ssi_dai[fifo0_channel]->private_data; - snd_soc_unregister_dai(dai); + snd_soc_unregister_dai(imx_ssi_dai[fifo0_channel]); + snd_soc_unregister_dai(imx_ssi_dai[fifo1_channel]); - if (ssi->flags & IMX_SSI_USE_AC97) - ac97_ssi = NULL; + kfree(imx_ssi_dai[fifo0_channel]->name); + kfree(imx_ssi_dai[fifo0_channel]); - 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); + kfree(imx_ssi_dai[fifo1_channel]->name); + kfree(imx_ssi_dai[fifo1_channel]); + clk_put(priv->ssi_clk); + iounmap(priv->ioaddr); + kfree(priv); return 0; } static struct platform_driver imx_ssi_driver = { - .probe = imx_ssi_probe, - .remove = __devexit_p(imx_ssi_remove), - + .probe = imx_ssi_dev_probe, + .remove = __devexit_p(imx_ssi_dev_remove), .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, + .name = "mxc_ssi", + }, }; static int __init imx_ssi_init(void) { - int ret; - - ret = snd_soc_register_platform(&imx_soc_platform); - if (ret) { - pr_err("failed to register soc platform: %d\n", ret); - return ret; - } - - ret = platform_driver_register(&imx_ssi_driver); - if (ret) { - snd_soc_unregister_platform(&imx_soc_platform); - return ret; - } - - return 0; + return platform_driver_register(&imx_ssi_driver); } static void __exit imx_ssi_exit(void) { platform_driver_unregister(&imx_ssi_driver); - snd_soc_unregister_platform(&imx_soc_platform); } module_init(imx_ssi_init); module_exit(imx_ssi_exit); - -/* Module information */ -MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>"); -MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); +MODULE_AUTHOR + ("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("i.MX ASoC I2S driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index 55f26ebcd8c2..6da50a5ae415 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -7,231 +7,238 @@ #ifndef _IMX_SSI_H #define _IMX_SSI_H -#define SSI_STX0 0x00 -#define SSI_STX1 0x04 -#define SSI_SRX0 0x08 -#define SSI_SRX1 0x0c - -#define SSI_SCR 0x10 -#define SSI_SCR_CLK_IST (1 << 9) -#define SSI_SCR_CLK_IST_SHIFT 9 -#define SSI_SCR_TCH_EN (1 << 8) -#define SSI_SCR_SYS_CLK_EN (1 << 7) -#define SSI_SCR_I2S_MODE_NORM (0 << 5) -#define SSI_SCR_I2S_MODE_MSTR (1 << 5) -#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) -#define SSI_I2S_MODE_MASK (3 << 5) -#define SSI_SCR_SYN (1 << 4) -#define SSI_SCR_NET (1 << 3) -#define SSI_SCR_RE (1 << 2) -#define SSI_SCR_TE (1 << 1) -#define SSI_SCR_SSIEN (1 << 0) - -#define SSI_SISR 0x14 -#define SSI_SISR_MASK ((1 << 19) - 1) -#define SSI_SISR_CMDAU (1 << 18) -#define SSI_SISR_CMDDU (1 << 17) -#define SSI_SISR_RXT (1 << 16) -#define SSI_SISR_RDR1 (1 << 15) -#define SSI_SISR_RDR0 (1 << 14) -#define SSI_SISR_TDE1 (1 << 13) -#define SSI_SISR_TDE0 (1 << 12) -#define SSI_SISR_ROE1 (1 << 11) -#define SSI_SISR_ROE0 (1 << 10) -#define SSI_SISR_TUE1 (1 << 9) -#define SSI_SISR_TUE0 (1 << 8) -#define SSI_SISR_TFS (1 << 7) -#define SSI_SISR_RFS (1 << 6) -#define SSI_SISR_TLS (1 << 5) -#define SSI_SISR_RLS (1 << 4) -#define SSI_SISR_RFF1 (1 << 3) -#define SSI_SISR_RFF0 (1 << 2) -#define SSI_SISR_TFE1 (1 << 1) -#define SSI_SISR_TFE0 (1 << 0) - -#define SSI_SIER 0x18 -#define SSI_SIER_RDMAE (1 << 22) -#define SSI_SIER_RIE (1 << 21) -#define SSI_SIER_TDMAE (1 << 20) -#define SSI_SIER_TIE (1 << 19) -#define SSI_SIER_CMDAU_EN (1 << 18) -#define SSI_SIER_CMDDU_EN (1 << 17) -#define SSI_SIER_RXT_EN (1 << 16) -#define SSI_SIER_RDR1_EN (1 << 15) -#define SSI_SIER_RDR0_EN (1 << 14) -#define SSI_SIER_TDE1_EN (1 << 13) -#define SSI_SIER_TDE0_EN (1 << 12) -#define SSI_SIER_ROE1_EN (1 << 11) -#define SSI_SIER_ROE0_EN (1 << 10) -#define SSI_SIER_TUE1_EN (1 << 9) -#define SSI_SIER_TUE0_EN (1 << 8) -#define SSI_SIER_TFS_EN (1 << 7) -#define SSI_SIER_RFS_EN (1 << 6) -#define SSI_SIER_TLS_EN (1 << 5) -#define SSI_SIER_RLS_EN (1 << 4) -#define SSI_SIER_RFF1_EN (1 << 3) -#define SSI_SIER_RFF0_EN (1 << 2) -#define SSI_SIER_TFE1_EN (1 << 1) -#define SSI_SIER_TFE0_EN (1 << 0) - -#define SSI_STCR 0x1c -#define SSI_STCR_TXBIT0 (1 << 9) -#define SSI_STCR_TFEN1 (1 << 8) -#define SSI_STCR_TFEN0 (1 << 7) -#define SSI_FIFO_ENABLE_0_SHIFT 7 -#define SSI_STCR_TFDIR (1 << 6) -#define SSI_STCR_TXDIR (1 << 5) -#define SSI_STCR_TSHFD (1 << 4) -#define SSI_STCR_TSCKP (1 << 3) -#define SSI_STCR_TFSI (1 << 2) -#define SSI_STCR_TFSL (1 << 1) -#define SSI_STCR_TEFS (1 << 0) - -#define SSI_SRCR 0x20 -#define SSI_SRCR_RXBIT0 (1 << 9) -#define SSI_SRCR_RFEN1 (1 << 8) -#define SSI_SRCR_RFEN0 (1 << 7) -#define SSI_FIFO_ENABLE_0_SHIFT 7 -#define SSI_SRCR_RFDIR (1 << 6) -#define SSI_SRCR_RXDIR (1 << 5) -#define SSI_SRCR_RSHFD (1 << 4) -#define SSI_SRCR_RSCKP (1 << 3) -#define SSI_SRCR_RFSI (1 << 2) -#define SSI_SRCR_RFSL (1 << 1) -#define SSI_SRCR_REFS (1 << 0) - -#define SSI_SRCCR 0x28 -#define SSI_SRCCR_DIV2 (1 << 18) -#define SSI_SRCCR_PSR (1 << 17) -#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) -#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) -#define SSI_SRCCR_WL_MASK (0xf << 13) -#define SSI_SRCCR_DC_MASK (0x1f << 8) -#define SSI_SRCCR_PM_MASK (0xff << 0) - -#define SSI_STCCR 0x24 -#define SSI_STCCR_DIV2 (1 << 18) -#define SSI_STCCR_PSR (1 << 17) -#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) -#define SSI_STCCR_PM(x) (((x) & 0xff) << 0) -#define SSI_STCCR_WL_MASK (0xf << 13) -#define SSI_STCCR_DC_MASK (0x1f << 8) -#define SSI_STCCR_PM_MASK (0xff << 0) - -#define SSI_SFCSR 0x2c -#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) -#define SSI_RX_FIFO_1_COUNT_SHIFT 28 -#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) -#define SSI_TX_FIFO_1_COUNT_SHIFT 24 -#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) -#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) -#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) -#define SSI_RX_FIFO_0_COUNT_SHIFT 12 -#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) -#define SSI_TX_FIFO_0_COUNT_SHIFT 8 -#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) -#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) -#define SSI_SFCSR_RFWM0_MASK (0xf << 4) -#define SSI_SFCSR_TFWM0_MASK (0xf << 0) - -#define SSI_STR 0x30 -#define SSI_STR_TEST (1 << 15) -#define SSI_STR_RCK2TCK (1 << 14) -#define SSI_STR_RFS2TFS (1 << 13) -#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) -#define SSI_STR_TXD2RXD (1 << 7) -#define SSI_STR_TCK2RCK (1 << 6) -#define SSI_STR_TFS2RFS (1 << 5) -#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) - -#define SSI_SOR 0x34 -#define SSI_SOR_CLKOFF (1 << 6) -#define SSI_SOR_RX_CLR (1 << 5) -#define SSI_SOR_TX_CLR (1 << 4) -#define SSI_SOR_INIT (1 << 3) -#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) -#define SSI_SOR_WAIT_MASK (0x3 << 1) -#define SSI_SOR_SYNRST (1 << 0) - -#define SSI_SACNT 0x38 -#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) -#define SSI_SACNT_WR (1 << 4) -#define SSI_SACNT_RD (1 << 3) -#define SSI_SACNT_TIF (1 << 2) -#define SSI_SACNT_FV (1 << 1) -#define SSI_SACNT_AC97EN (1 << 0) - -#define SSI_SACADD 0x3c -#define SSI_SACDAT 0x40 -#define SSI_SATAG 0x44 -#define SSI_STMSK 0x48 -#define SSI_SRMSK 0x4c -#define SSI_SACCST 0x50 -#define SSI_SACCEN 0x54 -#define SSI_SACCDIS 0x58 +#include <mach/hardware.h> + +/* SSI regs definition */ +#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR) +#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR) + +#define SSI1_STX0 ((SSI1_IO_BASE_ADDR) + 0x00) +#define SSI1_STX1 ((SSI1_IO_BASE_ADDR) + 0x04) +#define SSI1_SRX0 ((SSI1_IO_BASE_ADDR) + 0x08) +#define SSI1_SRX1 ((SSI1_IO_BASE_ADDR) + 0x0c) +#define SSI1_SCR ((SSI1_IO_BASE_ADDR) + 0x10) +#define SSI1_SISR ((SSI1_IO_BASE_ADDR) + 0x14) +#define SSI1_SIER ((SSI1_IO_BASE_ADDR) + 0x18) +#define SSI1_STCR ((SSI1_IO_BASE_ADDR) + 0x1c) +#define SSI1_SRCR ((SSI1_IO_BASE_ADDR) + 0x20) +#define SSI1_STCCR ((SSI1_IO_BASE_ADDR) + 0x24) +#define SSI1_SRCCR ((SSI1_IO_BASE_ADDR) + 0x28) +#define SSI1_SFCSR ((SSI1_IO_BASE_ADDR) + 0x2c) +#define SSI1_STR ((SSI1_IO_BASE_ADDR) + 0x30) +#define SSI1_SOR ((SSI1_IO_BASE_ADDR) + 0x34) +#define SSI1_SACNT ((SSI1_IO_BASE_ADDR) + 0x38) +#define SSI1_SACADD ((SSI1_IO_BASE_ADDR) + 0x3c) +#define SSI1_SACDAT ((SSI1_IO_BASE_ADDR) + 0x40) +#define SSI1_SATAG ((SSI1_IO_BASE_ADDR) + 0x44) +#define SSI1_STMSK ((SSI1_IO_BASE_ADDR) + 0x48) +#define SSI1_SRMSK ((SSI1_IO_BASE_ADDR) + 0x4c) +#define SSI1_SACCST ((SSI1_IO_BASE_ADDR) + 0x50) +#define SSI1_SACCEN ((SSI1_IO_BASE_ADDR) + 0x54) +#define SSI1_SACCDIS ((SSI1_IO_BASE_ADDR) + 0x58) + +#define SSI2_STX0 ((SSI2_IO_BASE_ADDR) + 0x00) +#define SSI2_STX1 ((SSI2_IO_BASE_ADDR) + 0x04) +#define SSI2_SRX0 ((SSI2_IO_BASE_ADDR) + 0x08) +#define SSI2_SRX1 ((SSI2_IO_BASE_ADDR) + 0x0c) +#define SSI2_SCR ((SSI2_IO_BASE_ADDR) + 0x10) +#define SSI2_SISR ((SSI2_IO_BASE_ADDR) + 0x14) +#define SSI2_SIER ((SSI2_IO_BASE_ADDR) + 0x18) +#define SSI2_STCR ((SSI2_IO_BASE_ADDR) + 0x1c) +#define SSI2_SRCR ((SSI2_IO_BASE_ADDR) + 0x20) +#define SSI2_STCCR ((SSI2_IO_BASE_ADDR) + 0x24) +#define SSI2_SRCCR ((SSI2_IO_BASE_ADDR) + 0x28) +#define SSI2_SFCSR ((SSI2_IO_BASE_ADDR) + 0x2c) +#define SSI2_STR ((SSI2_IO_BASE_ADDR) + 0x30) +#define SSI2_SOR ((SSI2_IO_BASE_ADDR) + 0x34) +#define SSI2_SACNT ((SSI2_IO_BASE_ADDR) + 0x38) +#define SSI2_SACADD ((SSI2_IO_BASE_ADDR) + 0x3c) +#define SSI2_SACDAT ((SSI2_IO_BASE_ADDR) + 0x40) +#define SSI2_SATAG ((SSI2_IO_BASE_ADDR) + 0x44) +#define SSI2_STMSK ((SSI2_IO_BASE_ADDR) + 0x48) +#define SSI2_SRMSK ((SSI2_IO_BASE_ADDR) + 0x4c) +#define SSI2_SACCST ((SSI2_IO_BASE_ADDR) + 0x50) +#define SSI2_SACCEN ((SSI2_IO_BASE_ADDR) + 0x54) +#define SSI2_SACCDIS ((SSI2_IO_BASE_ADDR) + 0x58) + +#define SSI_SCR_CLK_IST (1 << 9) +#define SSI_SCR_TCH_EN (1 << 8) +#define SSI_SCR_SYS_CLK_EN (1 << 7) +#define SSI_SCR_I2S_MODE_NORM (0 << 5) +#define SSI_SCR_I2S_MODE_MSTR (1 << 5) +#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) +#define SSI_SCR_SYN (1 << 4) +#define SSI_SCR_NET (1 << 3) +#define SSI_SCR_RE (1 << 2) +#define SSI_SCR_TE (1 << 1) +#define SSI_SCR_SSIEN (1 << 0) +#define SSI_SCR_I2S_MODE_MASK (3 << 5) + +#define SSI_SISR_CMDAU (1 << 18) +#define SSI_SISR_CMDDU (1 << 17) +#define SSI_SISR_RXT (1 << 16) +#define SSI_SISR_RDR1 (1 << 15) +#define SSI_SISR_RDR0 (1 << 14) +#define SSI_SISR_TDE1 (1 << 13) +#define SSI_SISR_TDE0 (1 << 12) +#define SSI_SISR_ROE1 (1 << 11) +#define SSI_SISR_ROE0 (1 << 10) +#define SSI_SISR_TUE1 (1 << 9) +#define SSI_SISR_TUE0 (1 << 8) +#define SSI_SISR_TFS (1 << 7) +#define SSI_SISR_RFS (1 << 6) +#define SSI_SISR_TLS (1 << 5) +#define SSI_SISR_RLS (1 << 4) +#define SSI_SISR_RFF1 (1 << 3) +#define SSI_SISR_RFF0 (1 << 2) +#define SSI_SISR_TFE1 (1 << 1) +#define SSI_SISR_TFE0 (1 << 0) + +#define SSI_SIER_RDMAE (1 << 22) +#define SSI_SIER_RIE (1 << 21) +#define SSI_SIER_TDMAE (1 << 20) +#define SSI_SIER_TIE (1 << 19) +#define SSI_SIER_CMDAU_EN (1 << 18) +#define SSI_SIER_CMDDU_EN (1 << 17) +#define SSI_SIER_RXT_EN (1 << 16) +#define SSI_SIER_RDR1_EN (1 << 15) +#define SSI_SIER_RDR0_EN (1 << 14) +#define SSI_SIER_TDE1_EN (1 << 13) +#define SSI_SIER_TDE0_EN (1 << 12) +#define SSI_SIER_ROE1_EN (1 << 11) +#define SSI_SIER_ROE0_EN (1 << 10) +#define SSI_SIER_TUE1_EN (1 << 9) +#define SSI_SIER_TUE0_EN (1 << 8) +#define SSI_SIER_TFS_EN (1 << 7) +#define SSI_SIER_RFS_EN (1 << 6) +#define SSI_SIER_TLS_EN (1 << 5) +#define SSI_SIER_RLS_EN (1 << 4) +#define SSI_SIER_RFF1_EN (1 << 3) +#define SSI_SIER_RFF0_EN (1 << 2) +#define SSI_SIER_TFE1_EN (1 << 1) +#define SSI_SIER_TFE0_EN (1 << 0) + +#define SSI_STCR_TXBIT0 (1 << 9) +#define SSI_STCR_TFEN1 (1 << 8) +#define SSI_STCR_TFEN0 (1 << 7) +#define SSI_STCR_TFDIR (1 << 6) +#define SSI_STCR_TXDIR (1 << 5) +#define SSI_STCR_TSHFD (1 << 4) +#define SSI_STCR_TSCKP (1 << 3) +#define SSI_STCR_TFSI (1 << 2) +#define SSI_STCR_TFSL (1 << 1) +#define SSI_STCR_TEFS (1 << 0) + +#define SSI_SRCR_RXBIT0 (1 << 9) +#define SSI_SRCR_RFEN1 (1 << 8) +#define SSI_SRCR_RFEN0 (1 << 7) +#define SSI_SRCR_RFDIR (1 << 6) +#define SSI_SRCR_RXDIR (1 << 5) +#define SSI_SRCR_RSHFD (1 << 4) +#define SSI_SRCR_RSCKP (1 << 3) +#define SSI_SRCR_RFSI (1 << 2) +#define SSI_SRCR_RFSL (1 << 1) +#define SSI_SRCR_REFS (1 << 0) + +#define SSI_STCCR_DIV2 (1 << 18) +#define SSI_STCCR_PSR (1 << 15) +#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) +#define SSI_STCCR_PM(x) (((x) & 0xff) << 0) +#define SSI_STCCR_WL_MASK (0xf << 13) +#define SSI_STCCR_DC_MASK (0x1f << 8) +#define SSI_STCCR_PM_MASK (0xff << 0) + +#define SSI_SRCCR_DIV2 (1 << 18) +#define SSI_SRCCR_PSR (1 << 15) +#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) +#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) +#define SSI_SRCCR_WL_MASK (0xf << 13) +#define SSI_SRCCR_DC_MASK (0x1f << 8) +#define SSI_SRCCR_PM_MASK (0xff << 0) + + +#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) +#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) +#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) +#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) +#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) +#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) +#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) +#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) + +#define SSI_STR_TEST (1 << 15) +#define SSI_STR_RCK2TCK (1 << 14) +#define SSI_STR_RFS2TFS (1 << 13) +#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) +#define SSI_STR_TXD2RXD (1 << 7) +#define SSI_STR_TCK2RCK (1 << 6) +#define SSI_STR_TFS2RFS (1 << 5) +#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) + +#define SSI_SOR_CLKOFF (1 << 6) +#define SSI_SOR_RX_CLR (1 << 5) +#define SSI_SOR_TX_CLR (1 << 4) +#define SSI_SOR_INIT (1 << 3) +#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) +#define SSI_SOR_SYNRST (1 << 0) + +#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) +#define SSI_SACNT_WR (1 << 4) +#define SSI_SACNT_RD (1 << 3) +#define SSI_SACNT_TIF (1 << 2) +#define SSI_SACNT_FV (1 << 1) +#define SSI_SACNT_AC97EN (1 << 0) + +/* SDMA & SSI watermarks for FIFO's */ +#define SDMA_TXFIFO_WATERMARK 0x4 +#define SDMA_RXFIFO_WATERMARK 0x6 +#define SSI_TXFIFO_WATERMARK 0x4 +#define SSI_RXFIFO_WATERMARK 0x6 + +/* Maximum number of ssi channels (counting two channels per block) */ +#define MAX_SSI_CHANNELS 8 + +/* 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 +#define IMX_SSP_SYS_CLK 0 /* SSI audio dividers */ -#define IMX_SSI_TX_DIV_2 0 -#define IMX_SSI_TX_DIV_PSR 1 -#define IMX_SSI_TX_DIV_PM 2 -#define IMX_SSI_RX_DIV_2 3 -#define IMX_SSI_RX_DIV_PSR 4 -#define IMX_SSI_RX_DIV_PM 5 - -extern struct snd_soc_dai imx_ssi_pcm_dai[2]; -extern struct snd_soc_platform imx_soc_platform; - -#define DRV_NAME "imx-ssi" - -struct imx_pcm_dma_params { - int dma; - unsigned long dma_addr; - int burstsize; -}; - -struct imx_ssi { - struct platform_device *ac97_dev; +#define IMX_SSI_TX_DIV_2 0 +#define IMX_SSI_TX_DIV_PSR 1 +#define IMX_SSI_TX_DIV_PM 2 +#define IMX_SSI_RX_DIV_2 3 +#define IMX_SSI_RX_DIV_PSR 4 +#define IMX_SSI_RX_DIV_PM 5 - struct snd_soc_device imx_ac97; - struct clk *clk; - void __iomem *base; - int irq; - int fiq_enable; - unsigned int offset; - - unsigned int flags; - void (*ac97_reset) (struct snd_ac97 *ac97); - void (*ac97_warm_reset)(struct snd_ac97 *ac97); +/* SSI Div 2 */ +#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2) +#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2 - struct imx_pcm_dma_params dma_params_rx; - struct imx_pcm_dma_params dma_params_tx; +#define IMX_DAI_AC97_1 0 +#define IMX_DAI_AC97_2 1 - int enabled; +/* private info */ +struct imx_ssi { + bool network_mode; + bool sync_mode; + unsigned int ac97_tx_slots; + unsigned int ac97_rx_slots; + void __iomem *ioaddr; + unsigned long baseaddr; + int irq; + struct platform_device *pdev; + struct clk *ssi_clk; }; -struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev, - struct imx_ssi *ssi); -void imx_ssi_fiq_exit(struct platform_device *pdev, struct imx_ssi *ssi); -struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev, - struct imx_ssi *ssi); - -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm); -void imx_pcm_free(struct snd_pcm *pcm); - -/* - * Do not change this as the FIQ handler depends on this size - */ -#define IMX_SSI_DMABUF_SIZE (64 * 1024) - -#define DMA_RXFIFO_BURST 0x4 -#define DMA_TXFIFO_BURST 0x6 +extern struct snd_soc_dai *imx_ssi_dai[]; +extern struct snd_soc_dai imx_ac97_dai[]; -#endif /* _IMX_SSI_H */ +#endif |