summaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r--sound/soc/fsl/Kconfig3
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_dma.c7
-rw-r--r--sound/soc/fsl/fsl_esai.c815
-rw-r--r--sound/soc/fsl/fsl_esai.h354
-rw-r--r--sound/soc/fsl/fsl_sai.c7
-rw-r--r--sound/soc/fsl/fsl_ssi.c586
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c3
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c3
-rw-r--r--sound/soc/fsl/mpc5200_dma.c4
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c3
11 files changed, 1579 insertions, 208 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 514c275c6108..324988dea4bd 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -8,6 +8,9 @@ config SND_SOC_FSL_SSI
config SND_SOC_FSL_SPDIF
tristate
+config SND_SOC_FSL_ESAI
+ tristate
+
config SND_SOC_FSL_UTILS
tristate
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index aaccbee17006..b12ad4b9b4da 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -14,11 +14,13 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-objs := fsl_ssi.o
snd-soc-fsl-spdif-objs := fsl_spdif.o
+snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
+obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index d570f8c81dc6..6bb0ea59284f 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -55,10 +55,6 @@
SNDRV_PCM_FMTBIT_S32_BE | \
SNDRV_PCM_FMTBIT_U32_LE | \
SNDRV_PCM_FMTBIT_U32_BE)
-
-#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
- SNDRV_PCM_RATE_CONTINUOUS)
-
struct dma_object {
struct snd_soc_platform_driver dai;
dma_addr_t ssi_stx_phys;
@@ -140,9 +136,6 @@ static const struct snd_pcm_hardware fsl_dma_hardware = {
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_PAUSE,
.formats = FSLDMA_PCM_FORMATS,
- .rates = FSLDMA_PCM_RATES,
- .rate_min = 5512,
- .rate_max = 192000,
.period_bytes_min = 512, /* A reasonable limit */
.period_bytes_max = (u32) -1,
.periods_min = NUM_DMA_LINKS,
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
new file mode 100644
index 000000000000..d0c72ed261e7
--- /dev/null
+++ b/sound/soc/fsl/fsl_esai.c
@@ -0,0 +1,815 @@
+/*
+ * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_esai.h"
+#include "imx-pcm.h"
+
+#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000
+#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/**
+ * fsl_esai: ESAI private data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @coreclk: clock source to access register
+ * @extalclk: esai clock source to derive HCK, SCK and FS
+ * @fsysclk: system clock source to derive HCK, SCK and FS
+ * @fifo_depth: depth of tx/rx FIFO
+ * @slot_width: width of each DAI slot
+ * @hck_rate: clock rate of desired HCKx clock
+ * @sck_div: if using PSR/PM dividers for SCKx clock
+ * @slave_mode: if fully using DAI slave mode
+ * @synchronous: if using tx/rx synchronous mode
+ * @name: driver name
+ */
+struct fsl_esai {
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct clk *coreclk;
+ struct clk *extalclk;
+ struct clk *fsysclk;
+ u32 fifo_depth;
+ u32 slot_width;
+ u32 hck_rate[2];
+ bool sck_div[2];
+ bool slave_mode;
+ bool synchronous;
+ char name[32];
+};
+
+static irqreturn_t esai_isr(int irq, void *devid)
+{
+ struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
+ struct platform_device *pdev = esai_priv->pdev;
+ u32 esr;
+
+ regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
+
+ if (esr & ESAI_ESR_TINIT_MASK)
+ dev_dbg(&pdev->dev, "isr: Transmition Initialized\n");
+
+ if (esr & ESAI_ESR_RFF_MASK)
+ dev_warn(&pdev->dev, "isr: Receiving overrun\n");
+
+ if (esr & ESAI_ESR_TFE_MASK)
+ dev_warn(&pdev->dev, "isr: Transmition underrun\n");
+
+ if (esr & ESAI_ESR_TLS_MASK)
+ dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
+
+ if (esr & ESAI_ESR_TDE_MASK)
+ dev_dbg(&pdev->dev, "isr: Transmition data exception\n");
+
+ if (esr & ESAI_ESR_TED_MASK)
+ dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
+
+ if (esr & ESAI_ESR_TD_MASK)
+ dev_dbg(&pdev->dev, "isr: Transmitting data\n");
+
+ if (esr & ESAI_ESR_RLS_MASK)
+ dev_dbg(&pdev->dev, "isr: Just received the last slot\n");
+
+ if (esr & ESAI_ESR_RDE_MASK)
+ dev_dbg(&pdev->dev, "isr: Receiving data exception\n");
+
+ if (esr & ESAI_ESR_RED_MASK)
+ dev_dbg(&pdev->dev, "isr: Receiving even slots\n");
+
+ if (esr & ESAI_ESR_RD_MASK)
+ dev_dbg(&pdev->dev, "isr: Receiving data\n");
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * This function is used to calculate the divisors of psr, pm, fp and it is
+ * supposed to be called in set_dai_sysclk() and set_bclk().
+ *
+ * @ratio: desired overall ratio for the paticipating dividers
+ * @usefp: for HCK setting, there is no need to set fp divider
+ * @fp: bypass other dividers by setting fp directly if fp != 0
+ * @tx: current setting is for playback or capture
+ */
+static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
+ bool usefp, u32 fp)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;
+
+ maxfp = usefp ? 16 : 1;
+
+ if (usefp && fp)
+ goto out_fp;
+
+ if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {
+ dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",
+ 2 * 8 * 256 * maxfp);
+ return -EINVAL;
+ } else if (ratio % 2) {
+ dev_err(dai->dev, "the raio must be even if using upper divider\n");
+ return -EINVAL;
+ }
+
+ ratio /= 2;
+
+ psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;
+
+ /* Set the max fluctuation -- 0.1% of the max devisor */
+ savesub = (psr ? 1 : 8) * 256 * maxfp / 1000;
+
+ /* Find the best value for PM */
+ for (i = 1; i <= 256; i++) {
+ for (j = 1; j <= maxfp; j++) {
+ /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */
+ prod = (psr ? 1 : 8) * i * j;
+
+ if (prod == ratio)
+ sub = 0;
+ else if (prod / ratio == 1)
+ sub = prod - ratio;
+ else if (ratio / prod == 1)
+ sub = ratio - prod;
+ else
+ continue;
+
+ /* Calculate the fraction */
+ sub = sub * 1000 / ratio;
+ if (sub < savesub) {
+ savesub = sub;
+ pm = i;
+ fp = j;
+ }
+
+ /* We are lucky */
+ if (savesub == 0)
+ goto out;
+ }
+ }
+
+ if (pm == 999) {
+ dev_err(dai->dev, "failed to calculate proper divisors\n");
+ return -EINVAL;
+ }
+
+out:
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
+ ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,
+ psr | ESAI_xCCR_xPM(pm));
+
+out_fp:
+ /* Bypass fp if not being required */
+ if (maxfp <= 1)
+ return 0;
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
+ ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));
+
+ return 0;
+}
+
+/**
+ * This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
+ *
+ * @Parameters:
+ * clk_id: The clock source of HCKT/HCKR
+ * (Input from outside; output from inside, FSYS or EXTAL)
+ * freq: The required clock rate of HCKT/HCKR
+ * dir: The clock direction of HCKT/HCKR
+ *
+ * Note: If the direction is input, we do not care about clk_id.
+ */
+static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ struct clk *clksrc = esai_priv->extalclk;
+ bool tx = clk_id <= ESAI_HCKT_EXTAL;
+ bool in = dir == SND_SOC_CLOCK_IN;
+ u32 ret, ratio, ecr = 0;
+ unsigned long clk_rate;
+
+ /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
+ esai_priv->sck_div[tx] = true;
+
+ /* Set the direction of HCKT/HCKR pins */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
+ ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);
+
+ if (in)
+ goto out;
+
+ switch (clk_id) {
+ case ESAI_HCKT_FSYS:
+ case ESAI_HCKR_FSYS:
+ clksrc = esai_priv->fsysclk;
+ break;
+ case ESAI_HCKT_EXTAL:
+ ecr |= ESAI_ECR_ETI;
+ case ESAI_HCKR_EXTAL:
+ ecr |= ESAI_ECR_ERI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (IS_ERR(clksrc)) {
+ dev_err(dai->dev, "no assigned %s clock\n",
+ clk_id % 2 ? "extal" : "fsys");
+ return PTR_ERR(clksrc);
+ }
+ clk_rate = clk_get_rate(clksrc);
+
+ ratio = clk_rate / freq;
+ if (ratio * freq > clk_rate)
+ ret = ratio * freq - clk_rate;
+ else if (ratio * freq < clk_rate)
+ ret = clk_rate - ratio * freq;
+ else
+ ret = 0;
+
+ /* Block if clock source can not be divided into the required rate */
+ if (ret != 0 && clk_rate / ret < 1000) {
+ dev_err(dai->dev, "failed to derive required HCK%c rate\n",
+ tx ? 'T' : 'R');
+ return -EINVAL;
+ }
+
+ if (ratio == 1) {
+ /* Bypass all the dividers if not being needed */
+ ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
+ goto out;
+ }
+
+ ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
+ if (ret)
+ return ret;
+
+ esai_priv->sck_div[tx] = false;
+
+out:
+ esai_priv->hck_rate[tx] = freq;
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
+ tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
+ ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);
+
+ return 0;
+}
+
+/**
+ * This function configures the related dividers according to the bclk rate
+ */
+static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ u32 hck_rate = esai_priv->hck_rate[tx];
+ u32 sub, ratio = hck_rate / freq;
+
+ /* Don't apply for fully slave mode*/
+ if (esai_priv->slave_mode)
+ return 0;
+
+ if (ratio * freq > hck_rate)
+ sub = ratio * freq - hck_rate;
+ else if (ratio * freq < hck_rate)
+ sub = hck_rate - ratio * freq;
+ else
+ sub = 0;
+
+ /* Block if clock source can not be divided into the required rate */
+ if (sub != 0 && hck_rate / sub < 1000) {
+ dev_err(dai->dev, "failed to derive required SCK%c rate\n",
+ tx ? 'T' : 'R');
+ return -EINVAL;
+ }
+
+ if (esai_priv->sck_div[tx] && (ratio > 16 || ratio == 0)) {
+ dev_err(dai->dev, "the ratio is out of range (1 ~ 16)\n");
+ return -EINVAL;
+ }
+
+ return fsl_esai_divisor_cal(dai, tx, ratio, true,
+ esai_priv->sck_div[tx] ? 0 : ratio);
+}
+
+static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
+ ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA,
+ ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB,
+ ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(tx_mask));
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
+ ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA,
+ ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB,
+ ESAI_xSMA_xS_MASK, ESAI_xSMB_xS(rx_mask));
+
+ esai_priv->slot_width = slot_width;
+
+ return 0;
+}
+
+static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ u32 xcr = 0, xccr = 0, mask;
+
+ /* 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 */
+ xcr |= ESAI_xCR_xFSR;
+ xccr |= ESAI_xCCR_xFSP | ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* Data on rising edge of bclk, frame high */
+ xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ /* Data on rising edge of bclk, frame high, right aligned */
+ xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCR_xWA;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* Data on rising edge of bclk, frame high, 1clk before data */
+ xcr |= ESAI_xCR_xFSL | ESAI_xCR_xFSR;
+ xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* Data on rising edge of bclk, frame high */
+ xcr |= ESAI_xCR_xFSL;
+ xccr |= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Nothing to do for both normal cases */
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ xccr ^= ESAI_xCCR_xFSP;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ xccr ^= ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ esai_priv->slave_mode = false;
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ esai_priv->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ xccr |= ESAI_xCCR_xCKD;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ xccr |= ESAI_xCCR_xFSD;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask = ESAI_xCR_xFSL | ESAI_xCR_xFSR;
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, xcr);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, mask, xcr);
+
+ mask = ESAI_xCCR_xCKP | ESAI_xCCR_xHCKP | ESAI_xCCR_xFSP |
+ ESAI_xCCR_xFSD | ESAI_xCCR_xCKD | ESAI_xCR_xWA;
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, mask, xccr);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, mask, xccr);
+
+ return 0;
+}
+
+static int fsl_esai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+
+ /*
+ * Some platforms might use the same bit to gate all three or two of
+ * clocks, so keep all clocks open/close at the same time for safety
+ */
+ clk_prepare_enable(esai_priv->coreclk);
+ if (!IS_ERR(esai_priv->extalclk))
+ clk_prepare_enable(esai_priv->extalclk);
+ if (!IS_ERR(esai_priv->fsysclk))
+ clk_prepare_enable(esai_priv->fsysclk);
+
+ if (!dai->active) {
+ /* Reset Port C */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC,
+ ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC,
+ ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO));
+
+ /* Set synchronous mode */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR,
+ ESAI_SAICR_SYNC, esai_priv->synchronous ?
+ ESAI_SAICR_SYNC : 0);
+
+ /* Set a default slot number -- 2 */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
+ ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
+ ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(2));
+ }
+
+ return 0;
+}
+
+static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 width = snd_pcm_format_width(params_format(params));
+ u32 channels = params_channels(params);
+ u32 bclk, mask, val, ret;
+
+ bclk = params_rate(params) * esai_priv->slot_width * 2;
+
+ ret = fsl_esai_set_bclk(dai, tx, bclk);
+ if (ret)
+ return ret;
+
+ /* Use Normal mode to support monaural audio */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ ESAI_xCR_xMOD_MASK, params_channels(params) > 1 ?
+ ESAI_xCR_xMOD_NETWORK : 0);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR_MASK, ESAI_xFCR_xFR);
+
+ mask = ESAI_xFCR_xFR_MASK | ESAI_xFCR_xWA_MASK | ESAI_xFCR_xFWM_MASK |
+ (tx ? ESAI_xFCR_TE_MASK | ESAI_xFCR_TIEN : ESAI_xFCR_RE_MASK);
+ val = ESAI_xFCR_xWA(width) | ESAI_xFCR_xFWM(esai_priv->fifo_depth) |
+ (tx ? ESAI_xFCR_TE(channels) | ESAI_xFCR_TIEN : ESAI_xFCR_RE(channels));
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
+
+ mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
+ val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
+
+ return 0;
+}
+
+static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+
+ if (!IS_ERR(esai_priv->fsysclk))
+ clk_disable_unprepare(esai_priv->fsysclk);
+ if (!IS_ERR(esai_priv->extalclk))
+ clk_disable_unprepare(esai_priv->extalclk);
+ clk_disable_unprepare(esai_priv->coreclk);
+}
+
+static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 i, channels = substream->runtime->channels;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN);
+
+ /* Write initial words reqiured by ESAI as normal procedure */
+ for (i = 0; tx && i < channels; i++)
+ regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
+
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
+ tx ? ESAI_xCR_TE(channels) : ESAI_xCR_RE(channels));
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
+ tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
+
+ /* Disable and reset FIFO */
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR);
+ regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
+ ESAI_xFCR_xFR, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops fsl_esai_dai_ops = {
+ .startup = fsl_esai_startup,
+ .shutdown = fsl_esai_shutdown,
+ .trigger = fsl_esai_trigger,
+ .hw_params = fsl_esai_hw_params,
+ .set_sysclk = fsl_esai_set_dai_sysclk,
+ .set_fmt = fsl_esai_set_dai_fmt,
+ .set_tdm_slot = fsl_esai_set_dai_tdm_slot,
+};
+
+static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &esai_priv->dma_params_tx,
+ &esai_priv->dma_params_rx);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver fsl_esai_dai = {
+ .probe = fsl_esai_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 12,
+ .rates = FSL_ESAI_RATES,
+ .formats = FSL_ESAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = FSL_ESAI_RATES,
+ .formats = FSL_ESAI_FORMATS,
+ },
+ .ops = &fsl_esai_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_esai_component = {
+ .name = "fsl-esai",
+};
+
+static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ESAI_ERDR:
+ case REG_ESAI_ECR:
+ case REG_ESAI_ESR:
+ case REG_ESAI_TFCR:
+ case REG_ESAI_TFSR:
+ case REG_ESAI_RFCR:
+ case REG_ESAI_RFSR:
+ case REG_ESAI_RX0:
+ case REG_ESAI_RX1:
+ case REG_ESAI_RX2:
+ case REG_ESAI_RX3:
+ case REG_ESAI_SAISR:
+ case REG_ESAI_SAICR:
+ case REG_ESAI_TCR:
+ case REG_ESAI_TCCR:
+ case REG_ESAI_RCR:
+ case REG_ESAI_RCCR:
+ case REG_ESAI_TSMA:
+ case REG_ESAI_TSMB:
+ case REG_ESAI_RSMA:
+ case REG_ESAI_RSMB:
+ case REG_ESAI_PRRC:
+ case REG_ESAI_PCRC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ESAI_ETDR:
+ case REG_ESAI_ECR:
+ case REG_ESAI_TFCR:
+ case REG_ESAI_RFCR:
+ case REG_ESAI_TX0:
+ case REG_ESAI_TX1:
+ case REG_ESAI_TX2:
+ case REG_ESAI_TX3:
+ case REG_ESAI_TX4:
+ case REG_ESAI_TX5:
+ case REG_ESAI_TSR:
+ case REG_ESAI_SAICR:
+ case REG_ESAI_TCR:
+ case REG_ESAI_TCCR:
+ case REG_ESAI_RCR:
+ case REG_ESAI_RCCR:
+ case REG_ESAI_TSMA:
+ case REG_ESAI_TSMB:
+ case REG_ESAI_RSMA:
+ case REG_ESAI_RSMB:
+ case REG_ESAI_PRRC:
+ case REG_ESAI_PCRC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config fsl_esai_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_ESAI_PCRC,
+ .readable_reg = fsl_esai_readable_reg,
+ .writeable_reg = fsl_esai_writeable_reg,
+};
+
+static int fsl_esai_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_esai *esai_priv;
+ struct resource *res;
+ const uint32_t *iprop;
+ void __iomem *regs;
+ int irq, ret;
+
+ esai_priv = devm_kzalloc(&pdev->dev, sizeof(*esai_priv), GFP_KERNEL);
+ if (!esai_priv)
+ return -ENOMEM;
+
+ esai_priv->pdev = pdev;
+ strcpy(esai_priv->name, np->name);
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ esai_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core", regs, &fsl_esai_regmap_config);
+ if (IS_ERR(esai_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap: %ld\n",
+ PTR_ERR(esai_priv->regmap));
+ return PTR_ERR(esai_priv->regmap);
+ }
+
+ esai_priv->coreclk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(esai_priv->coreclk)) {
+ dev_err(&pdev->dev, "failed to get core clock: %ld\n",
+ PTR_ERR(esai_priv->coreclk));
+ return PTR_ERR(esai_priv->coreclk);
+ }
+
+ esai_priv->extalclk = devm_clk_get(&pdev->dev, "extal");
+ if (IS_ERR(esai_priv->extalclk))
+ dev_warn(&pdev->dev, "failed to get extal clock: %ld\n",
+ PTR_ERR(esai_priv->extalclk));
+
+ esai_priv->fsysclk = devm_clk_get(&pdev->dev, "fsys");
+ if (IS_ERR(esai_priv->fsysclk))
+ dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
+ PTR_ERR(esai_priv->fsysclk));
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0,
+ esai_priv->name, esai_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
+ return ret;
+ }
+
+ /* Set a default slot size */
+ esai_priv->slot_width = 32;
+
+ /* Set a default master/slave state */
+ esai_priv->slave_mode = true;
+
+ /* Determine the FIFO depth */
+ iprop = of_get_property(np, "fsl,fifo-depth", NULL);
+ if (iprop)
+ esai_priv->fifo_depth = be32_to_cpup(iprop);
+ else
+ esai_priv->fifo_depth = 64;
+
+ esai_priv->dma_params_tx.maxburst = 16;
+ esai_priv->dma_params_rx.maxburst = 16;
+ esai_priv->dma_params_tx.addr = res->start + REG_ESAI_ETDR;
+ esai_priv->dma_params_rx.addr = res->start + REG_ESAI_ERDR;
+
+ esai_priv->synchronous =
+ of_property_read_bool(np, "fsl,esai-synchronous");
+
+ /* Implement full symmetry for synchronous mode */
+ if (esai_priv->synchronous) {
+ fsl_esai_dai.symmetric_rates = 1;
+ fsl_esai_dai.symmetric_channels = 1;
+ fsl_esai_dai.symmetric_samplebits = 1;
+ }
+
+ dev_set_drvdata(&pdev->dev, esai_priv);
+
+ /* Reset ESAI unit */
+ ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * We need to enable ESAI so as to access some of its registers.
+ * Otherwise, we would fail to dump regmap from user space.
+ */
+ ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
+ &fsl_esai_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ return ret;
+ }
+
+ ret = imx_pcm_dma_init(pdev);
+ if (ret)
+ dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id fsl_esai_dt_ids[] = {
+ { .compatible = "fsl,imx35-esai", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
+
+static struct platform_driver fsl_esai_driver = {
+ .probe = fsl_esai_probe,
+ .driver = {
+ .name = "fsl-esai-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_esai_dt_ids,
+ },
+};
+
+module_platform_driver(fsl_esai_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale ESAI CPU DAI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-esai-dai");
diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h
new file mode 100644
index 000000000000..9c9f957fcae1
--- /dev/null
+++ b/sound/soc/fsl/fsl_esai.h
@@ -0,0 +1,354 @@
+/*
+ * fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_ESAI_DAI_H
+#define _FSL_ESAI_DAI_H
+
+/* ESAI Register Map */
+#define REG_ESAI_ETDR 0x00
+#define REG_ESAI_ERDR 0x04
+#define REG_ESAI_ECR 0x08
+#define REG_ESAI_ESR 0x0C
+#define REG_ESAI_TFCR 0x10
+#define REG_ESAI_TFSR 0x14
+#define REG_ESAI_RFCR 0x18
+#define REG_ESAI_RFSR 0x1C
+#define REG_ESAI_xFCR(tx) (tx ? REG_ESAI_TFCR : REG_ESAI_RFCR)
+#define REG_ESAI_xFSR(tx) (tx ? REG_ESAI_TFSR : REG_ESAI_RFSR)
+#define REG_ESAI_TX0 0x80
+#define REG_ESAI_TX1 0x84
+#define REG_ESAI_TX2 0x88
+#define REG_ESAI_TX3 0x8C
+#define REG_ESAI_TX4 0x90
+#define REG_ESAI_TX5 0x94
+#define REG_ESAI_TSR 0x98
+#define REG_ESAI_RX0 0xA0
+#define REG_ESAI_RX1 0xA4
+#define REG_ESAI_RX2 0xA8
+#define REG_ESAI_RX3 0xAC
+#define REG_ESAI_SAISR 0xCC
+#define REG_ESAI_SAICR 0xD0
+#define REG_ESAI_TCR 0xD4
+#define REG_ESAI_TCCR 0xD8
+#define REG_ESAI_RCR 0xDC
+#define REG_ESAI_RCCR 0xE0
+#define REG_ESAI_xCR(tx) (tx ? REG_ESAI_TCR : REG_ESAI_RCR)
+#define REG_ESAI_xCCR(tx) (tx ? REG_ESAI_TCCR : REG_ESAI_RCCR)
+#define REG_ESAI_TSMA 0xE4
+#define REG_ESAI_TSMB 0xE8
+#define REG_ESAI_RSMA 0xEC
+#define REG_ESAI_RSMB 0xF0
+#define REG_ESAI_xSMA(tx) (tx ? REG_ESAI_TSMA : REG_ESAI_RSMA)
+#define REG_ESAI_xSMB(tx) (tx ? REG_ESAI_TSMB : REG_ESAI_RSMB)
+#define REG_ESAI_PRRC 0xF8
+#define REG_ESAI_PCRC 0xFC
+
+/* ESAI Control Register -- REG_ESAI_ECR 0x8 */
+#define ESAI_ECR_ETI_SHIFT 19
+#define ESAI_ECR_ETI_MASK (1 << ESAI_ECR_ETI_SHIFT)
+#define ESAI_ECR_ETI (1 << ESAI_ECR_ETI_SHIFT)
+#define ESAI_ECR_ETO_SHIFT 18
+#define ESAI_ECR_ETO_MASK (1 << ESAI_ECR_ETO_SHIFT)
+#define ESAI_ECR_ETO (1 << ESAI_ECR_ETO_SHIFT)
+#define ESAI_ECR_ERI_SHIFT 17
+#define ESAI_ECR_ERI_MASK (1 << ESAI_ECR_ERI_SHIFT)
+#define ESAI_ECR_ERI (1 << ESAI_ECR_ERI_SHIFT)
+#define ESAI_ECR_ERO_SHIFT 16
+#define ESAI_ECR_ERO_MASK (1 << ESAI_ECR_ERO_SHIFT)
+#define ESAI_ECR_ERO (1 << ESAI_ECR_ERO_SHIFT)
+#define ESAI_ECR_ERST_SHIFT 1
+#define ESAI_ECR_ERST_MASK (1 << ESAI_ECR_ERST_SHIFT)
+#define ESAI_ECR_ERST (1 << ESAI_ECR_ERST_SHIFT)
+#define ESAI_ECR_ESAIEN_SHIFT 0
+#define ESAI_ECR_ESAIEN_MASK (1 << ESAI_ECR_ESAIEN_SHIFT)
+#define ESAI_ECR_ESAIEN (1 << ESAI_ECR_ESAIEN_SHIFT)
+
+/* ESAI Status Register -- REG_ESAI_ESR 0xC */
+#define ESAI_ESR_TINIT_SHIFT 10
+#define ESAI_ESR_TINIT_MASK (1 << ESAI_ESR_TINIT_SHIFT)
+#define ESAI_ESR_TINIT (1 << ESAI_ESR_TINIT_SHIFT)
+#define ESAI_ESR_RFF_SHIFT 9
+#define ESAI_ESR_RFF_MASK (1 << ESAI_ESR_RFF_SHIFT)
+#define ESAI_ESR_RFF (1 << ESAI_ESR_RFF_SHIFT)
+#define ESAI_ESR_TFE_SHIFT 8
+#define ESAI_ESR_TFE_MASK (1 << ESAI_ESR_TFE_SHIFT)
+#define ESAI_ESR_TFE (1 << ESAI_ESR_TFE_SHIFT)
+#define ESAI_ESR_TLS_SHIFT 7
+#define ESAI_ESR_TLS_MASK (1 << ESAI_ESR_TLS_SHIFT)
+#define ESAI_ESR_TLS (1 << ESAI_ESR_TLS_SHIFT)
+#define ESAI_ESR_TDE_SHIFT 6
+#define ESAI_ESR_TDE_MASK (1 << ESAI_ESR_TDE_SHIFT)
+#define ESAI_ESR_TDE (1 << ESAI_ESR_TDE_SHIFT)
+#define ESAI_ESR_TED_SHIFT 5
+#define ESAI_ESR_TED_MASK (1 << ESAI_ESR_TED_SHIFT)
+#define ESAI_ESR_TED (1 << ESAI_ESR_TED_SHIFT)
+#define ESAI_ESR_TD_SHIFT 4
+#define ESAI_ESR_TD_MASK (1 << ESAI_ESR_TD_SHIFT)
+#define ESAI_ESR_TD (1 << ESAI_ESR_TD_SHIFT)
+#define ESAI_ESR_RLS_SHIFT 3
+#define ESAI_ESR_RLS_MASK (1 << ESAI_ESR_RLS_SHIFT)
+#define ESAI_ESR_RLS (1 << ESAI_ESR_RLS_SHIFT)
+#define ESAI_ESR_RDE_SHIFT 2
+#define ESAI_ESR_RDE_MASK (1 << ESAI_ESR_RDE_SHIFT)
+#define ESAI_ESR_RDE (1 << ESAI_ESR_RDE_SHIFT)
+#define ESAI_ESR_RED_SHIFT 1
+#define ESAI_ESR_RED_MASK (1 << ESAI_ESR_RED_SHIFT)
+#define ESAI_ESR_RED (1 << ESAI_ESR_RED_SHIFT)
+#define ESAI_ESR_RD_SHIFT 0
+#define ESAI_ESR_RD_MASK (1 << ESAI_ESR_RD_SHIFT)
+#define ESAI_ESR_RD (1 << ESAI_ESR_RD_SHIFT)
+
+/*
+ * Transmit FIFO Configuration Register -- REG_ESAI_TFCR 0x10
+ * Receive FIFO Configuration Register -- REG_ESAI_RFCR 0x18
+ */
+#define ESAI_xFCR_TIEN_SHIFT 19
+#define ESAI_xFCR_TIEN_MASK (1 << ESAI_xFCR_TIEN_SHIFT)
+#define ESAI_xFCR_TIEN (1 << ESAI_xFCR_TIEN_SHIFT)
+#define ESAI_xFCR_REXT_SHIFT 19
+#define ESAI_xFCR_REXT_MASK (1 << ESAI_xFCR_REXT_SHIFT)
+#define ESAI_xFCR_REXT (1 << ESAI_xFCR_REXT_SHIFT)
+#define ESAI_xFCR_xWA_SHIFT 16
+#define ESAI_xFCR_xWA_WIDTH 3
+#define ESAI_xFCR_xWA_MASK (((1 << ESAI_xFCR_xWA_WIDTH) - 1) << ESAI_xFCR_xWA_SHIFT)
+#define ESAI_xFCR_xWA(v) (((8 - ((v) >> 2)) << ESAI_xFCR_xWA_SHIFT) & ESAI_xFCR_xWA_MASK)
+#define ESAI_xFCR_xFWM_SHIFT 8
+#define ESAI_xFCR_xFWM_WIDTH 8
+#define ESAI_xFCR_xFWM_MASK (((1 << ESAI_xFCR_xFWM_WIDTH) - 1) << ESAI_xFCR_xFWM_SHIFT)
+#define ESAI_xFCR_xFWM(v) ((((v) - 1) << ESAI_xFCR_xFWM_SHIFT) & ESAI_xFCR_xFWM_MASK)
+#define ESAI_xFCR_xE_SHIFT 2
+#define ESAI_xFCR_TE_WIDTH 6
+#define ESAI_xFCR_RE_WIDTH 4
+#define ESAI_xFCR_TE_MASK (((1 << ESAI_xFCR_TE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
+#define ESAI_xFCR_RE_MASK (((1 << ESAI_xFCR_RE_WIDTH) - 1) << ESAI_xFCR_xE_SHIFT)
+#define ESAI_xFCR_TE(x) ((ESAI_xFCR_TE_MASK >> (ESAI_xFCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_TE_MASK)
+#define ESAI_xFCR_RE(x) ((ESAI_xFCR_RE_MASK >> (ESAI_xFCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xFCR_RE_MASK)
+#define ESAI_xFCR_xFR_SHIFT 1
+#define ESAI_xFCR_xFR_MASK (1 << ESAI_xFCR_xFR_SHIFT)
+#define ESAI_xFCR_xFR (1 << ESAI_xFCR_xFR_SHIFT)
+#define ESAI_xFCR_xFEN_SHIFT 0
+#define ESAI_xFCR_xFEN_MASK (1 << ESAI_xFCR_xFEN_SHIFT)
+#define ESAI_xFCR_xFEN (1 << ESAI_xFCR_xFEN_SHIFT)
+
+/*
+ * Transmit FIFO Status Register -- REG_ESAI_TFSR 0x14
+ * Receive FIFO Status Register --REG_ESAI_RFSR 0x1C
+ */
+#define ESAI_xFSR_NTFO_SHIFT 12
+#define ESAI_xFSR_NRFI_SHIFT 12
+#define ESAI_xFSR_NTFI_SHIFT 8
+#define ESAI_xFSR_NRFO_SHIFT 8
+#define ESAI_xFSR_NTFx_WIDTH 3
+#define ESAI_xFSR_NRFx_WIDTH 2
+#define ESAI_xFSR_NTFO_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFO_SHIFT)
+#define ESAI_xFSR_NTFI_MASK (((1 << ESAI_xFSR_NTFx_WIDTH) - 1) << ESAI_xFSR_NTFI_SHIFT)
+#define ESAI_xFSR_NRFO_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFO_SHIFT)
+#define ESAI_xFSR_NRFI_MASK (((1 << ESAI_xFSR_NRFx_WIDTH) - 1) << ESAI_xFSR_NRFI_SHIFT)
+#define ESAI_xFSR_xFCNT_SHIFT 0
+#define ESAI_xFSR_xFCNT_WIDTH 8
+#define ESAI_xFSR_xFCNT_MASK (((1 << ESAI_xFSR_xFCNT_WIDTH) - 1) << ESAI_xFSR_xFCNT_SHIFT)
+
+/* ESAI Transmit Slot Register -- REG_ESAI_TSR 0x98 */
+#define ESAI_TSR_SHIFT 0
+#define ESAI_TSR_WIDTH 24
+#define ESAI_TSR_MASK (((1 << ESAI_TSR_WIDTH) - 1) << ESAI_TSR_SHIFT)
+
+/* Serial Audio Interface Status Register -- REG_ESAI_SAISR 0xCC */
+#define ESAI_SAISR_TODFE_SHIFT 17
+#define ESAI_SAISR_TODFE_MASK (1 << ESAI_SAISR_TODFE_SHIFT)
+#define ESAI_SAISR_TODFE (1 << ESAI_SAISR_TODFE_SHIFT)
+#define ESAI_SAISR_TEDE_SHIFT 16
+#define ESAI_SAISR_TEDE_MASK (1 << ESAI_SAISR_TEDE_SHIFT)
+#define ESAI_SAISR_TEDE (1 << ESAI_SAISR_TEDE_SHIFT)
+#define ESAI_SAISR_TDE_SHIFT 15
+#define ESAI_SAISR_TDE_MASK (1 << ESAI_SAISR_TDE_SHIFT)
+#define ESAI_SAISR_TDE (1 << ESAI_SAISR_TDE_SHIFT)
+#define ESAI_SAISR_TUE_SHIFT 14
+#define ESAI_SAISR_TUE_MASK (1 << ESAI_SAISR_TUE_SHIFT)
+#define ESAI_SAISR_TUE (1 << ESAI_SAISR_TUE_SHIFT)
+#define ESAI_SAISR_TFS_SHIFT 13
+#define ESAI_SAISR_TFS_MASK (1 << ESAI_SAISR_TFS_SHIFT)
+#define ESAI_SAISR_TFS (1 << ESAI_SAISR_TFS_SHIFT)
+#define ESAI_SAISR_RODF_SHIFT 10
+#define ESAI_SAISR_RODF_MASK (1 << ESAI_SAISR_RODF_SHIFT)
+#define ESAI_SAISR_RODF (1 << ESAI_SAISR_RODF_SHIFT)
+#define ESAI_SAISR_REDF_SHIFT 9
+#define ESAI_SAISR_REDF_MASK (1 << ESAI_SAISR_REDF_SHIFT)
+#define ESAI_SAISR_REDF (1 << ESAI_SAISR_REDF_SHIFT)
+#define ESAI_SAISR_RDF_SHIFT 8
+#define ESAI_SAISR_RDF_MASK (1 << ESAI_SAISR_RDF_SHIFT)
+#define ESAI_SAISR_RDF (1 << ESAI_SAISR_RDF_SHIFT)
+#define ESAI_SAISR_ROE_SHIFT 7
+#define ESAI_SAISR_ROE_MASK (1 << ESAI_SAISR_ROE_SHIFT)
+#define ESAI_SAISR_ROE (1 << ESAI_SAISR_ROE_SHIFT)
+#define ESAI_SAISR_RFS_SHIFT 6
+#define ESAI_SAISR_RFS_MASK (1 << ESAI_SAISR_RFS_SHIFT)
+#define ESAI_SAISR_RFS (1 << ESAI_SAISR_RFS_SHIFT)
+#define ESAI_SAISR_IF2_SHIFT 2
+#define ESAI_SAISR_IF2_MASK (1 << ESAI_SAISR_IF2_SHIFT)
+#define ESAI_SAISR_IF2 (1 << ESAI_SAISR_IF2_SHIFT)
+#define ESAI_SAISR_IF1_SHIFT 1
+#define ESAI_SAISR_IF1_MASK (1 << ESAI_SAISR_IF1_SHIFT)
+#define ESAI_SAISR_IF1 (1 << ESAI_SAISR_IF1_SHIFT)
+#define ESAI_SAISR_IF0_SHIFT 0
+#define ESAI_SAISR_IF0_MASK (1 << ESAI_SAISR_IF0_SHIFT)
+#define ESAI_SAISR_IF0 (1 << ESAI_SAISR_IF0_SHIFT)
+
+/* Serial Audio Interface Control Register -- REG_ESAI_SAICR 0xD0 */
+#define ESAI_SAICR_ALC_SHIFT 8
+#define ESAI_SAICR_ALC_MASK (1 << ESAI_SAICR_ALC_SHIFT)
+#define ESAI_SAICR_ALC (1 << ESAI_SAICR_ALC_SHIFT)
+#define ESAI_SAICR_TEBE_SHIFT 7
+#define ESAI_SAICR_TEBE_MASK (1 << ESAI_SAICR_TEBE_SHIFT)
+#define ESAI_SAICR_TEBE (1 << ESAI_SAICR_TEBE_SHIFT)
+#define ESAI_SAICR_SYNC_SHIFT 6
+#define ESAI_SAICR_SYNC_MASK (1 << ESAI_SAICR_SYNC_SHIFT)
+#define ESAI_SAICR_SYNC (1 << ESAI_SAICR_SYNC_SHIFT)
+#define ESAI_SAICR_OF2_SHIFT 2
+#define ESAI_SAICR_OF2_MASK (1 << ESAI_SAICR_OF2_SHIFT)
+#define ESAI_SAICR_OF2 (1 << ESAI_SAICR_OF2_SHIFT)
+#define ESAI_SAICR_OF1_SHIFT 1
+#define ESAI_SAICR_OF1_MASK (1 << ESAI_SAICR_OF1_SHIFT)
+#define ESAI_SAICR_OF1 (1 << ESAI_SAICR_OF1_SHIFT)
+#define ESAI_SAICR_OF0_SHIFT 0
+#define ESAI_SAICR_OF0_MASK (1 << ESAI_SAICR_OF0_SHIFT)
+#define ESAI_SAICR_OF0 (1 << ESAI_SAICR_OF0_SHIFT)
+
+/*
+ * Transmit Control Register -- REG_ESAI_TCR 0xD4
+ * Receive Control Register -- REG_ESAI_RCR 0xDC
+ */
+#define ESAI_xCR_xLIE_SHIFT 23
+#define ESAI_xCR_xLIE_MASK (1 << ESAI_xCR_xLIE_SHIFT)
+#define ESAI_xCR_xLIE (1 << ESAI_xCR_xLIE_SHIFT)
+#define ESAI_xCR_xIE_SHIFT 22
+#define ESAI_xCR_xIE_MASK (1 << ESAI_xCR_xIE_SHIFT)
+#define ESAI_xCR_xIE (1 << ESAI_xCR_xIE_SHIFT)
+#define ESAI_xCR_xEDIE_SHIFT 21
+#define ESAI_xCR_xEDIE_MASK (1 << ESAI_xCR_xEDIE_SHIFT)
+#define ESAI_xCR_xEDIE (1 << ESAI_xCR_xEDIE_SHIFT)
+#define ESAI_xCR_xEIE_SHIFT 20
+#define ESAI_xCR_xEIE_MASK (1 << ESAI_xCR_xEIE_SHIFT)
+#define ESAI_xCR_xEIE (1 << ESAI_xCR_xEIE_SHIFT)
+#define ESAI_xCR_xPR_SHIFT 19
+#define ESAI_xCR_xPR_MASK (1 << ESAI_xCR_xPR_SHIFT)
+#define ESAI_xCR_xPR (1 << ESAI_xCR_xPR_SHIFT)
+#define ESAI_xCR_PADC_SHIFT 17
+#define ESAI_xCR_PADC_MASK (1 << ESAI_xCR_PADC_SHIFT)
+#define ESAI_xCR_PADC (1 << ESAI_xCR_PADC_SHIFT)
+#define ESAI_xCR_xFSR_SHIFT 16
+#define ESAI_xCR_xFSR_MASK (1 << ESAI_xCR_xFSR_SHIFT)
+#define ESAI_xCR_xFSR (1 << ESAI_xCR_xFSR_SHIFT)
+#define ESAI_xCR_xFSL_SHIFT 15
+#define ESAI_xCR_xFSL_MASK (1 << ESAI_xCR_xFSL_SHIFT)
+#define ESAI_xCR_xFSL (1 << ESAI_xCR_xFSL_SHIFT)
+#define ESAI_xCR_xSWS_SHIFT 10
+#define ESAI_xCR_xSWS_WIDTH 5
+#define ESAI_xCR_xSWS_MASK (((1 << ESAI_xCR_xSWS_WIDTH) - 1) << ESAI_xCR_xSWS_SHIFT)
+#define ESAI_xCR_xSWS(s, w) ((w < 24 ? (s - w + ((w - 8) >> 2)) : (s < 32 ? 0x1e : 0x1f)) << ESAI_xCR_xSWS_SHIFT)
+#define ESAI_xCR_xMOD_SHIFT 8
+#define ESAI_xCR_xMOD_WIDTH 2
+#define ESAI_xCR_xMOD_MASK (((1 << ESAI_xCR_xMOD_WIDTH) - 1) << ESAI_xCR_xMOD_SHIFT)
+#define ESAI_xCR_xMOD_ONDEMAND (0x1 << ESAI_xCR_xMOD_SHIFT)
+#define ESAI_xCR_xMOD_NETWORK (0x1 << ESAI_xCR_xMOD_SHIFT)
+#define ESAI_xCR_xMOD_AC97 (0x3 << ESAI_xCR_xMOD_SHIFT)
+#define ESAI_xCR_xWA_SHIFT 7
+#define ESAI_xCR_xWA_MASK (1 << ESAI_xCR_xWA_SHIFT)
+#define ESAI_xCR_xWA (1 << ESAI_xCR_xWA_SHIFT)
+#define ESAI_xCR_xSHFD_SHIFT 6
+#define ESAI_xCR_xSHFD_MASK (1 << ESAI_xCR_xSHFD_SHIFT)
+#define ESAI_xCR_xSHFD (1 << ESAI_xCR_xSHFD_SHIFT)
+#define ESAI_xCR_xE_SHIFT 0
+#define ESAI_xCR_TE_WIDTH 6
+#define ESAI_xCR_RE_WIDTH 4
+#define ESAI_xCR_TE_MASK (((1 << ESAI_xCR_TE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
+#define ESAI_xCR_RE_MASK (((1 << ESAI_xCR_RE_WIDTH) - 1) << ESAI_xCR_xE_SHIFT)
+#define ESAI_xCR_TE(x) ((ESAI_xCR_TE_MASK >> (ESAI_xCR_TE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_TE_MASK)
+#define ESAI_xCR_RE(x) ((ESAI_xCR_RE_MASK >> (ESAI_xCR_RE_WIDTH - ((x + 1) >> 1))) & ESAI_xCR_RE_MASK)
+
+/*
+ * Transmit Clock Control Register -- REG_ESAI_TCCR 0xD8
+ * Receive Clock Control Register -- REG_ESAI_RCCR 0xE0
+ */
+#define ESAI_xCCR_xHCKD_SHIFT 23
+#define ESAI_xCCR_xHCKD_MASK (1 << ESAI_xCCR_xHCKD_SHIFT)
+#define ESAI_xCCR_xHCKD (1 << ESAI_xCCR_xHCKD_SHIFT)
+#define ESAI_xCCR_xFSD_SHIFT 22
+#define ESAI_xCCR_xFSD_MASK (1 << ESAI_xCCR_xFSD_SHIFT)
+#define ESAI_xCCR_xFSD (1 << ESAI_xCCR_xFSD_SHIFT)
+#define ESAI_xCCR_xCKD_SHIFT 21
+#define ESAI_xCCR_xCKD_MASK (1 << ESAI_xCCR_xCKD_SHIFT)
+#define ESAI_xCCR_xCKD (1 << ESAI_xCCR_xCKD_SHIFT)
+#define ESAI_xCCR_xHCKP_SHIFT 20
+#define ESAI_xCCR_xHCKP_MASK (1 << ESAI_xCCR_xHCKP_SHIFT)
+#define ESAI_xCCR_xHCKP (1 << ESAI_xCCR_xHCKP_SHIFT)
+#define ESAI_xCCR_xFSP_SHIFT 19
+#define ESAI_xCCR_xFSP_MASK (1 << ESAI_xCCR_xFSP_SHIFT)
+#define ESAI_xCCR_xFSP (1 << ESAI_xCCR_xFSP_SHIFT)
+#define ESAI_xCCR_xCKP_SHIFT 18
+#define ESAI_xCCR_xCKP_MASK (1 << ESAI_xCCR_xCKP_SHIFT)
+#define ESAI_xCCR_xCKP (1 << ESAI_xCCR_xCKP_SHIFT)
+#define ESAI_xCCR_xFP_SHIFT 14
+#define ESAI_xCCR_xFP_WIDTH 4
+#define ESAI_xCCR_xFP_MASK (((1 << ESAI_xCCR_xFP_WIDTH) - 1) << ESAI_xCCR_xFP_SHIFT)
+#define ESAI_xCCR_xFP(v) ((((v) - 1) << ESAI_xCCR_xFP_SHIFT) & ESAI_xCCR_xFP_MASK)
+#define ESAI_xCCR_xDC_SHIFT 9
+#define ESAI_xCCR_xDC_WIDTH 4
+#define ESAI_xCCR_xDC_MASK (((1 << ESAI_xCCR_xDC_WIDTH) - 1) << ESAI_xCCR_xDC_SHIFT)
+#define ESAI_xCCR_xDC(v) ((((v) - 1) << ESAI_xCCR_xDC_SHIFT) & ESAI_xCCR_xDC_MASK)
+#define ESAI_xCCR_xPSR_SHIFT 8
+#define ESAI_xCCR_xPSR_MASK (1 << ESAI_xCCR_xPSR_SHIFT)
+#define ESAI_xCCR_xPSR_BYPASS (1 << ESAI_xCCR_xPSR_SHIFT)
+#define ESAI_xCCR_xPSR_DIV8 (0 << ESAI_xCCR_xPSR_SHIFT)
+#define ESAI_xCCR_xPM_SHIFT 0
+#define ESAI_xCCR_xPM_WIDTH 8
+#define ESAI_xCCR_xPM_MASK (((1 << ESAI_xCCR_xPM_WIDTH) - 1) << ESAI_xCCR_xPM_SHIFT)
+#define ESAI_xCCR_xPM(v) ((((v) - 1) << ESAI_xCCR_xPM_SHIFT) & ESAI_xCCR_xPM_MASK)
+
+/* Transmit Slot Mask Register A/B -- REG_ESAI_TSMA/B 0xE4 ~ 0xF0 */
+#define ESAI_xSMA_xS_SHIFT 0
+#define ESAI_xSMA_xS_WIDTH 16
+#define ESAI_xSMA_xS_MASK (((1 << ESAI_xSMA_xS_WIDTH) - 1) << ESAI_xSMA_xS_SHIFT)
+#define ESAI_xSMA_xS(v) ((v) & ESAI_xSMA_xS_MASK)
+#define ESAI_xSMB_xS_SHIFT 0
+#define ESAI_xSMB_xS_WIDTH 16
+#define ESAI_xSMB_xS_MASK (((1 << ESAI_xSMB_xS_WIDTH) - 1) << ESAI_xSMB_xS_SHIFT)
+#define ESAI_xSMB_xS(v) (((v) >> ESAI_xSMA_xS_WIDTH) & ESAI_xSMA_xS_MASK)
+
+/* Port C Direction Register -- REG_ESAI_PRRC 0xF8 */
+#define ESAI_PRRC_PDC_SHIFT 0
+#define ESAI_PRRC_PDC_WIDTH 12
+#define ESAI_PRRC_PDC_MASK (((1 << ESAI_PRRC_PDC_WIDTH) - 1) << ESAI_PRRC_PDC_SHIFT)
+#define ESAI_PRRC_PDC(v) ((v) & ESAI_PRRC_PDC_MASK)
+
+/* Port C Control Register -- REG_ESAI_PCRC 0xFC */
+#define ESAI_PCRC_PC_SHIFT 0
+#define ESAI_PCRC_PC_WIDTH 12
+#define ESAI_PCRC_PC_MASK (((1 << ESAI_PCRC_PC_WIDTH) - 1) << ESAI_PCRC_PC_SHIFT)
+#define ESAI_PCRC_PC(v) ((v) & ESAI_PCRC_PC_MASK)
+
+#define ESAI_GPIO 0xfff
+
+/* ESAI clock source */
+#define ESAI_HCKT_FSYS 0
+#define ESAI_HCKT_EXTAL 1
+#define ESAI_HCKR_FSYS 2
+#define ESAI_HCKR_EXTAL 3
+
+/* ESAI clock divider */
+#define ESAI_TX_DIV_PSR 0
+#define ESAI_TX_DIV_PM 1
+#define ESAI_TX_DIV_FP 2
+#define ESAI_RX_DIV_PSR 3
+#define ESAI_RX_DIV_PM 4
+#define ESAI_RX_DIV_FP 5
+#endif /* _FSL_ESAI_DAI_H */
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 5d38a6749b9f..cdd3fa830704 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -62,26 +62,25 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
reg_cr2 = FSL_SAI_RCR2;
val_cr2 = sai_readl(sai, sai->base + reg_cr2);
+ val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+
switch (clk_id) {
case FSL_SAI_CLK_BUS:
- val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
break;
case FSL_SAI_CLK_MAST1:
- val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
break;
case FSL_SAI_CLK_MAST2:
- val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
break;
case FSL_SAI_CLK_MAST3:
- val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
break;
default:
return -EINVAL;
}
+
sai_writel(sai, val_cr2, sai->base + reg_cr2);
return 0;
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index b2ebaf811599..f9090b167ad7 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -35,6 +35,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -80,8 +81,7 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
* ALSA that we support all rates and let the codec driver decide what rates
* are really supported.
*/
-#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
- SNDRV_PCM_RATE_CONTINUOUS)
+#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
/**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
@@ -107,12 +107,33 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
#endif
-/* SIER bitflag of interrupts to enable */
-#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
- CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
- CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
- CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
- CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
+#define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \
+ CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \
+ CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN)
+#define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \
+ CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \
+ CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN)
+#define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS)
+
+
+enum fsl_ssi_type {
+ FSL_SSI_MCP8610,
+ FSL_SSI_MX21,
+ FSL_SSI_MX35,
+ FSL_SSI_MX51,
+};
+
+struct fsl_ssi_reg_val {
+ u32 sier;
+ u32 srcr;
+ u32 stcr;
+ u32 scr;
+};
+
+struct fsl_ssi_rxtx_reg_val {
+ struct fsl_ssi_reg_val rx;
+ struct fsl_ssi_reg_val tx;
+};
/**
* fsl_ssi_private: per-SSI private data
@@ -133,14 +154,16 @@ struct fsl_ssi_private {
unsigned int irq;
unsigned int fifo_depth;
struct snd_soc_dai_driver cpu_dai_drv;
- struct device_attribute dev_attr;
struct platform_device *pdev;
+ enum fsl_ssi_type hw_type;
bool new_binding;
bool ssi_on_imx;
bool imx_ac97;
bool use_dma;
bool baudclk_locked;
+ bool irq_stats;
+ bool offline_config;
u8 i2s_mode;
spinlock_t baudclk_lock;
struct clk *baudclk;
@@ -150,6 +173,8 @@ struct fsl_ssi_private {
struct imx_dma_data filter_data_tx;
struct imx_dma_data filter_data_rx;
struct imx_pcm_fiq_params fiq_params;
+ /* Register values for rx/tx configuration */
+ struct fsl_ssi_rxtx_reg_val rxtx_reg_val;
struct {
unsigned int rfrc;
@@ -174,10 +199,21 @@ struct fsl_ssi_private {
unsigned int tfe1;
unsigned int tfe0;
} stats;
+ struct dentry *dbg_dir;
+ struct dentry *dbg_stats;
char name[1];
};
+static const struct of_device_id fsl_ssi_ids[] = {
+ { .compatible = "fsl,mpc8610-ssi", .data = (void *) FSL_SSI_MCP8610},
+ { .compatible = "fsl,imx51-ssi", .data = (void *) FSL_SSI_MX51},
+ { .compatible = "fsl,imx35-ssi", .data = (void *) FSL_SSI_MX35},
+ { .compatible = "fsl,imx21-ssi", .data = (void *) FSL_SSI_MX21},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
+
/**
* fsl_ssi_isr: SSI interrupt handler
*
@@ -196,23 +232,40 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
irqreturn_t ret = IRQ_NONE;
__be32 sisr;
- __be32 sisr2 = 0;
+ __be32 sisr2;
+ __be32 sisr_write_mask = 0;
+
+ switch (ssi_private->hw_type) {
+ case FSL_SSI_MX21:
+ sisr_write_mask = 0;
+ break;
+
+ case FSL_SSI_MCP8610:
+ case FSL_SSI_MX35:
+ sisr_write_mask = CCSR_SSI_SISR_RFRC | CCSR_SSI_SISR_TFRC |
+ CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
+ CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
+ break;
+
+ case FSL_SSI_MX51:
+ sisr_write_mask = CCSR_SSI_SISR_ROE0 | CCSR_SSI_SISR_ROE1 |
+ CCSR_SSI_SISR_TUE0 | CCSR_SSI_SISR_TUE1;
+ break;
+ }
/* We got an interrupt, so read the status register to see what we
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
- sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
+ sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK;
if (sisr & CCSR_SSI_SISR_RFRC) {
ssi_private->stats.rfrc++;
- sisr2 |= CCSR_SSI_SISR_RFRC;
ret = IRQ_HANDLED;
}
if (sisr & CCSR_SSI_SISR_TFRC) {
ssi_private->stats.tfrc++;
- sisr2 |= CCSR_SSI_SISR_TFRC;
ret = IRQ_HANDLED;
}
@@ -253,25 +306,21 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
if (sisr & CCSR_SSI_SISR_ROE1) {
ssi_private->stats.roe1++;
- sisr2 |= CCSR_SSI_SISR_ROE1;
ret = IRQ_HANDLED;
}
if (sisr & CCSR_SSI_SISR_ROE0) {
ssi_private->stats.roe0++;
- sisr2 |= CCSR_SSI_SISR_ROE0;
ret = IRQ_HANDLED;
}
if (sisr & CCSR_SSI_SISR_TUE1) {
ssi_private->stats.tue1++;
- sisr2 |= CCSR_SSI_SISR_TUE1;
ret = IRQ_HANDLED;
}
if (sisr & CCSR_SSI_SISR_TUE0) {
ssi_private->stats.tue0++;
- sisr2 |= CCSR_SSI_SISR_TUE0;
ret = IRQ_HANDLED;
}
@@ -315,6 +364,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
ret = IRQ_HANDLED;
}
+ sisr2 = sisr & sisr_write_mask;
/* Clear the bits that we set */
if (sisr2)
write_ssi(sisr2, &ssi->sisr);
@@ -322,6 +372,245 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
return ret;
}
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+/* Show the statistics of a flag only if its interrupt is enabled. The
+ * compiler will optimze this code to a no-op if the interrupt is not
+ * enabled.
+ */
+#define SIER_SHOW(flag, name) \
+ do { \
+ if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \
+ seq_printf(s, #name "=%u\n", ssi_private->stats.name); \
+ } while (0)
+
+
+/**
+ * fsl_sysfs_ssi_show: display SSI statistics
+ *
+ * Display the statistics for the current SSI device. To avoid confusion,
+ * we only show those counts that are enabled.
+ */
+static int fsl_ssi_stats_show(struct seq_file *s, void *unused)
+{
+ struct fsl_ssi_private *ssi_private = s->private;
+
+ SIER_SHOW(RFRC_EN, rfrc);
+ SIER_SHOW(TFRC_EN, tfrc);
+ SIER_SHOW(CMDAU_EN, cmdau);
+ SIER_SHOW(CMDDU_EN, cmddu);
+ SIER_SHOW(RXT_EN, rxt);
+ SIER_SHOW(RDR1_EN, rdr1);
+ SIER_SHOW(RDR0_EN, rdr0);
+ SIER_SHOW(TDE1_EN, tde1);
+ SIER_SHOW(TDE0_EN, tde0);
+ SIER_SHOW(ROE1_EN, roe1);
+ SIER_SHOW(ROE0_EN, roe0);
+ SIER_SHOW(TUE1_EN, tue1);
+ SIER_SHOW(TUE0_EN, tue0);
+ SIER_SHOW(TFS_EN, tfs);
+ SIER_SHOW(RFS_EN, rfs);
+ SIER_SHOW(TLS_EN, tls);
+ SIER_SHOW(RLS_EN, rls);
+ SIER_SHOW(RFF1_EN, rff1);
+ SIER_SHOW(RFF0_EN, rff0);
+ SIER_SHOW(TFE1_EN, tfe1);
+ SIER_SHOW(TFE0_EN, tfe0);
+
+ return 0;
+}
+
+static int fsl_ssi_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fsl_ssi_stats_show, inode->i_private);
+}
+
+static const struct file_operations fsl_ssi_stats_ops = {
+ .open = fsl_ssi_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
+ struct device *dev)
+{
+ ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+ if (!ssi_private->dbg_dir)
+ return -ENOMEM;
+
+ ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO,
+ ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops);
+ if (!ssi_private->dbg_stats) {
+ debugfs_remove(ssi_private->dbg_dir);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
+{
+ debugfs_remove(ssi_private->dbg_stats);
+ debugfs_remove(ssi_private->dbg_dir);
+}
+
+#else
+
+static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private,
+ struct device *dev)
+{
+ return 0;
+}
+
+static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
+
+/*
+ * Enable/Disable all rx/tx config flags at once.
+ */
+static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
+ bool enable)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ struct fsl_ssi_rxtx_reg_val *vals = &ssi_private->rxtx_reg_val;
+
+ if (enable) {
+ write_ssi_mask(&ssi->sier, 0, vals->rx.sier | vals->tx.sier);
+ write_ssi_mask(&ssi->srcr, 0, vals->rx.srcr | vals->tx.srcr);
+ write_ssi_mask(&ssi->stcr, 0, vals->rx.stcr | vals->tx.stcr);
+ } else {
+ write_ssi_mask(&ssi->srcr, vals->rx.srcr | vals->tx.srcr, 0);
+ write_ssi_mask(&ssi->stcr, vals->rx.stcr | vals->tx.stcr, 0);
+ write_ssi_mask(&ssi->sier, vals->rx.sier | vals->tx.sier, 0);
+ }
+}
+
+/*
+ * Enable/Disable a ssi configuration. You have to pass either
+ * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
+ */
+static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
+ struct fsl_ssi_reg_val *vals)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ struct fsl_ssi_reg_val *avals;
+ u32 scr_val = read_ssi(&ssi->scr);
+ int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
+ !!(scr_val & CCSR_SSI_SCR_RE);
+
+ /* Find the other direction values rx or tx which we do not want to
+ * modify */
+ if (&ssi_private->rxtx_reg_val.rx == vals)
+ avals = &ssi_private->rxtx_reg_val.tx;
+ else
+ avals = &ssi_private->rxtx_reg_val.rx;
+
+ /* If vals should be disabled, start with disabling the unit */
+ if (!enable) {
+ u32 scr = vals->scr & (vals->scr ^ avals->scr);
+ write_ssi_mask(&ssi->scr, scr, 0);
+ }
+
+ /*
+ * We are running on a SoC which does not support online SSI
+ * reconfiguration, so we have to enable all necessary flags at once
+ * even if we do not use them later (capture and playback configuration)
+ */
+ if (ssi_private->offline_config) {
+ if ((enable && !nr_active_streams) ||
+ (!enable && nr_active_streams == 1))
+ fsl_ssi_rxtx_config(ssi_private, enable);
+
+ goto config_done;
+ }
+
+ /*
+ * Configure single direction units while the SSI unit is running
+ * (online configuration)
+ */
+ if (enable) {
+ write_ssi_mask(&ssi->sier, 0, vals->sier);
+ write_ssi_mask(&ssi->srcr, 0, vals->srcr);
+ write_ssi_mask(&ssi->stcr, 0, vals->stcr);
+ } else {
+ u32 sier;
+ u32 srcr;
+ u32 stcr;
+
+ /*
+ * Disabling the necessary flags for one of rx/tx while the
+ * other stream is active is a little bit more difficult. We
+ * have to disable only those flags that differ between both
+ * streams (rx XOR tx) and that are set in the stream that is
+ * disabled now. Otherwise we could alter flags of the other
+ * stream
+ */
+
+ /* These assignments are simply vals without bits set in avals*/
+ sier = vals->sier & (vals->sier ^ avals->sier);
+ srcr = vals->srcr & (vals->srcr ^ avals->srcr);
+ stcr = vals->stcr & (vals->stcr ^ avals->stcr);
+
+ write_ssi_mask(&ssi->srcr, srcr, 0);
+ write_ssi_mask(&ssi->stcr, stcr, 0);
+ write_ssi_mask(&ssi->sier, sier, 0);
+ }
+
+config_done:
+ /* Enabling of subunits is done after configuration */
+ if (enable)
+ write_ssi_mask(&ssi->scr, 0, vals->scr);
+}
+
+
+static void fsl_ssi_rx_config(struct fsl_ssi_private *ssi_private, bool enable)
+{
+ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.rx);
+}
+
+static void fsl_ssi_tx_config(struct fsl_ssi_private *ssi_private, bool enable)
+{
+ fsl_ssi_config(ssi_private, enable, &ssi_private->rxtx_reg_val.tx);
+}
+
+/*
+ * Setup rx/tx register values used to enable/disable the streams. These will
+ * be used later in fsl_ssi_config to setup the streams without the need to
+ * check for all different SSI modes.
+ */
+static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
+{
+ struct fsl_ssi_rxtx_reg_val *reg = &ssi_private->rxtx_reg_val;
+
+ reg->rx.sier = CCSR_SSI_SIER_RFF0_EN;
+ reg->rx.srcr = CCSR_SSI_SRCR_RFEN0;
+ reg->rx.scr = 0;
+ reg->tx.sier = CCSR_SSI_SIER_TFE0_EN;
+ reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
+ reg->tx.scr = 0;
+
+ if (!ssi_private->imx_ac97) {
+ reg->rx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE;
+ reg->rx.sier |= CCSR_SSI_SIER_RFF0_EN;
+ reg->tx.scr = CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE;
+ reg->tx.sier |= CCSR_SSI_SIER_TFE0_EN;
+ }
+
+ if (ssi_private->use_dma) {
+ reg->rx.sier |= CCSR_SSI_SIER_RDMAE;
+ reg->tx.sier |= CCSR_SSI_SIER_TDMAE;
+ } else {
+ reg->rx.sier |= CCSR_SSI_SIER_RIE;
+ reg->tx.sier |= CCSR_SSI_SIER_TIE;
+ }
+
+ reg->rx.sier |= FSLSSI_SIER_DBG_RX_FLAGS;
+ reg->tx.sier |= FSLSSI_SIER_DBG_TX_FLAGS;
+}
+
static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
{
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
@@ -358,6 +647,8 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
u8 wm;
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+ fsl_ssi_setup_reg_vals(ssi_private);
+
if (ssi_private->imx_ac97)
ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
else
@@ -381,13 +672,12 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
ssi_private->i2s_mode |
(synchronous ? CCSR_SSI_SCR_SYN : 0));
- write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
- CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
- CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+ write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFSI |
+ CCSR_SSI_STCR_TEFS | CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+
+ write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFSI |
+ CCSR_SSI_SRCR_REFS | CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
- write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
- CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
- CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
/*
* The DC and PM bits are only used if the SSI is the clock master.
*/
@@ -420,6 +710,17 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
if (ssi_private->imx_ac97)
fsl_ssi_setup_ac97(ssi_private);
+ /*
+ * Set a default slot number so that there is no need for those common
+ * cases like I2S mode to call the extra set_tdm_slot() any more.
+ */
+ if (!ssi_private->imx_ac97) {
+ write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
+ write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
+ }
+
return 0;
}
@@ -762,50 +1063,26 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
- unsigned int sier_bits;
unsigned long flags;
- /*
- * Enable only the interrupts and DMA requests
- * that are needed for the channel. As the fiq
- * is polling for this bits, we have to ensure
- * that this are aligned with the preallocated
- * buffers
- */
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (ssi_private->use_dma)
- sier_bits = SIER_FLAGS;
- else
- sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
- } else {
- if (ssi_private->use_dma)
- sier_bits = SIER_FLAGS;
- else
- sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
- }
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- write_ssi_mask(&ssi->scr, 0,
- CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
+ fsl_ssi_tx_config(ssi_private, true);
else
- write_ssi_mask(&ssi->scr, 0,
- CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
+ fsl_ssi_rx_config(ssi_private, true);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
+ fsl_ssi_tx_config(ssi_private, false);
else
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
+ fsl_ssi_rx_config(ssi_private, false);
if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
(CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
ssi_private->baudclk_locked = false;
spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
@@ -816,7 +1093,12 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
}
- write_ssi(sier_bits, &ssi->sier);
+ if (ssi_private->imx_ac97) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
+ else
+ write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
+ }
return 0;
}
@@ -864,58 +1146,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
.name = "fsl-ssi",
};
-/**
- * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
- *
- * This function is called by ALSA to start, stop, pause, and resume the
- * transfer of data.
- */
-static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(
- rtd->cpu_dai);
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
- CCSR_SSI_SIER_TFE0_EN);
- else
- write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
- CCSR_SSI_SIER_RFF0_EN);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
- CCSR_SSI_SIER_TFE0_EN, 0);
- else
- write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
- CCSR_SSI_SIER_RFF0_EN, 0);
- break;
-
- default:
- return -EINVAL;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
- else
- write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
- .startup = fsl_ssi_startup,
- .trigger = fsl_ssi_ac97_trigger,
-};
-
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.ac97_control = 1,
.playback = {
@@ -932,7 +1162,7 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &fsl_ssi_ac97_dai_ops,
+ .ops = &fsl_ssi_dai_ops,
};
@@ -990,56 +1220,6 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
.write = fsl_ssi_ac97_write,
};
-/* Show the statistics of a flag only if its interrupt is enabled. The
- * compiler will optimze this code to a no-op if the interrupt is not
- * enabled.
- */
-#define SIER_SHOW(flag, name) \
- do { \
- if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
- length += sprintf(buf + length, #name "=%u\n", \
- ssi_private->stats.name); \
- } while (0)
-
-
-/**
- * fsl_sysfs_ssi_show: display SSI statistics
- *
- * Display the statistics for the current SSI device. To avoid confusion,
- * we only show those counts that are enabled.
- */
-static ssize_t fsl_sysfs_ssi_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct fsl_ssi_private *ssi_private =
- container_of(attr, struct fsl_ssi_private, dev_attr);
- ssize_t length = 0;
-
- SIER_SHOW(RFRC_EN, rfrc);
- SIER_SHOW(TFRC_EN, tfrc);
- SIER_SHOW(CMDAU_EN, cmdau);
- SIER_SHOW(CMDDU_EN, cmddu);
- SIER_SHOW(RXT_EN, rxt);
- SIER_SHOW(RDR1_EN, rdr1);
- SIER_SHOW(RDR0_EN, rdr0);
- SIER_SHOW(TDE1_EN, tde1);
- SIER_SHOW(TDE0_EN, tde0);
- SIER_SHOW(ROE1_EN, roe1);
- SIER_SHOW(ROE0_EN, roe0);
- SIER_SHOW(TUE1_EN, tue1);
- SIER_SHOW(TUE0_EN, tue0);
- SIER_SHOW(TFS_EN, tfs);
- SIER_SHOW(RFS_EN, rfs);
- SIER_SHOW(TLS_EN, tls);
- SIER_SHOW(RLS_EN, rls);
- SIER_SHOW(RFF1_EN, rff1);
- SIER_SHOW(RFF0_EN, rff0);
- SIER_SHOW(TFE1_EN, tfe1);
- SIER_SHOW(TFE0_EN, tfe0);
-
- return length;
-}
-
/**
* Make every character in a string lower-case
*/
@@ -1061,6 +1241,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
int ret = 0;
struct device_attribute *dev_attr = NULL;
struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id;
+ enum fsl_ssi_type hw_type;
const char *p, *sprop;
const uint32_t *iprop;
struct resource res;
@@ -1075,6 +1257,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (!of_device_is_available(np))
return -ENODEV;
+ of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
+ hw_type = (enum fsl_ssi_type) of_id->data;
+
/* We only support the SSI in "I2S Slave" mode */
sprop = of_get_property(np, "fsl,mode", NULL);
if (!sprop) {
@@ -1101,6 +1288,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ssi_private->use_dma = !of_property_read_bool(np,
"fsl,fiq-stream-filter");
+ ssi_private->hw_type = hw_type;
if (ac97) {
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
@@ -1154,7 +1342,34 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ssi_private->baudclk_locked = false;
spin_lock_init(&ssi_private->baudclk_lock);
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
+ /*
+ * imx51 and later SoCs have a slightly different IP that allows the
+ * SSI configuration while the SSI unit is running.
+ *
+ * More important, it is necessary on those SoCs to configure the
+ * sperate TX/RX DMA bits just before starting the stream
+ * (fsl_ssi_trigger). The SDMA unit has to be configured before fsl_ssi
+ * sends any DMA requests to the SDMA unit, otherwise it is not defined
+ * how the SDMA unit handles the DMA request.
+ *
+ * SDMA units are present on devices starting at imx35 but the imx35
+ * reference manual states that the DMA bits should not be changed
+ * while the SSI unit is running (SSIEN). So we support the necessary
+ * online configuration of fsl-ssi starting at imx51.
+ */
+ switch (hw_type) {
+ case FSL_SSI_MCP8610:
+ case FSL_SSI_MX21:
+ case FSL_SSI_MX35:
+ ssi_private->offline_config = true;
+ break;
+ case FSL_SSI_MX51:
+ ssi_private->offline_config = false;
+ break;
+ }
+
+ if (hw_type == FSL_SSI_MX21 || hw_type == FSL_SSI_MX51 ||
+ hw_type == FSL_SSI_MX35) {
u32 dma_events[2];
ssi_private->ssi_on_imx = true;
@@ -1176,7 +1391,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
*/
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
if (IS_ERR(ssi_private->baudclk))
- dev_warn(&pdev->dev, "could not get baud clock: %d\n", ret);
+ dev_warn(&pdev->dev, "could not get baud clock: %ld\n",
+ PTR_ERR(ssi_private->baudclk));
else
clk_prepare_enable(ssi_private->baudclk);
@@ -1218,32 +1434,25 @@ static int fsl_ssi_probe(struct platform_device *pdev)
dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
- } else if (ssi_private->use_dma) {
+ }
+
+ /*
+ * Enable interrupts only for MCP8610 and MX51. The other MXs have
+ * different writeable interrupt status registers.
+ */
+ if (ssi_private->use_dma) {
/* The 'name' should not have any slashes in it. */
ret = devm_request_irq(&pdev->dev, ssi_private->irq,
fsl_ssi_isr, 0, ssi_private->name,
ssi_private);
+ ssi_private->irq_stats = true;
if (ret < 0) {
dev_err(&pdev->dev, "could not claim irq %u\n",
ssi_private->irq);
- goto error_irqmap;
+ goto error_clk;
}
}
- /* Initialize the the device_attribute structure */
- dev_attr = &ssi_private->dev_attr;
- sysfs_attr_init(&dev_attr->attr);
- dev_attr->attr.name = "statistics";
- dev_attr->attr.mode = S_IRUGO;
- dev_attr->show = fsl_sysfs_ssi_show;
-
- ret = device_create_file(&pdev->dev, dev_attr);
- if (ret) {
- dev_err(&pdev->dev, "could not create sysfs %s file\n",
- ssi_private->dev_attr.attr.name);
- goto error_clk;
- }
-
/* Register with ASoC */
dev_set_drvdata(&pdev->dev, ssi_private);
@@ -1254,6 +1463,10 @@ static int fsl_ssi_probe(struct platform_device *pdev)
goto error_dev;
}
+ ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
+ if (ret)
+ goto error_dbgfs;
+
if (ssi_private->ssi_on_imx) {
if (!ssi_private->use_dma) {
@@ -1273,11 +1486,11 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
if (ret)
- goto error_dev;
+ goto error_pcm;
} else {
ret = imx_pcm_dma_init(pdev);
if (ret)
- goto error_dev;
+ goto error_pcm;
}
}
@@ -1319,6 +1532,13 @@ done:
return 0;
error_dai:
+ if (ssi_private->ssi_on_imx && !ssi_private->use_dma)
+ imx_pcm_fiq_exit(pdev);
+
+error_pcm:
+ fsl_ssi_debugfs_remove(ssi_private);
+
+error_dbgfs:
snd_soc_unregister_component(&pdev->dev);
error_dev:
@@ -1332,7 +1552,8 @@ error_clk:
}
error_irqmap:
- irq_dispose_mapping(ssi_private->irq);
+ if (ssi_private->irq_stats)
+ irq_dispose_mapping(ssi_private->irq);
return ret;
}
@@ -1341,27 +1562,22 @@ static int fsl_ssi_remove(struct platform_device *pdev)
{
struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
+ fsl_ssi_debugfs_remove(ssi_private);
+
if (!ssi_private->new_binding)
platform_device_unregister(ssi_private->pdev);
snd_soc_unregister_component(&pdev->dev);
- device_remove_file(&pdev->dev, &ssi_private->dev_attr);
if (ssi_private->ssi_on_imx) {
if (!IS_ERR(ssi_private->baudclk))
clk_disable_unprepare(ssi_private->baudclk);
clk_disable_unprepare(ssi_private->clk);
}
- irq_dispose_mapping(ssi_private->irq);
+ if (ssi_private->irq_stats)
+ irq_dispose_mapping(ssi_private->irq);
return 0;
}
-static const struct of_device_id fsl_ssi_ids[] = {
- { .compatible = "fsl,mpc8610-ssi", },
- { .compatible = "fsl,imx21-ssi", },
- {}
-};
-MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
-
static struct platform_driver fsl_ssi_driver = {
.driver = {
.name = "fsl-ssi-dai",
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index c5e47f866b4b..2585ae44e634 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -41,9 +41,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
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 = 65535, /* Limited by SDMA engine */
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index c75d43bb2e92..6553202dd48c 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -162,9 +162,6 @@ static struct snd_pcm_hardware snd_imx_hardware = {
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,
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index 71bf2f248cd4..f2b5d756b1f3 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -200,10 +200,6 @@ static const struct snd_pcm_hardware psc_dma_hardware = {
SNDRV_PCM_INFO_BATCH,
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
- .rate_min = 8000,
- .rate_max = 48000,
- .channels_min = 1,
- .channels_max = 2,
.period_bytes_max = 1024 * 1024,
.period_bytes_min = 32,
.periods_min = 2,
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index f4efaadb80a2..5d07e8a74a21 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -26,8 +26,7 @@
* ALSA that we support all rates and let the codec driver decide what rates
* are really supported.
*/
-#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
- SNDRV_PCM_RATE_CONTINUOUS)
+#define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
/**
* PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode