OSDN Git Service

Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl', 'asoc/topic/fsl...
authorMark Brown <broonie@linaro.org>
Wed, 21 May 2014 23:23:51 +0000 (00:23 +0100)
committerMark Brown <broonie@linaro.org>
Wed, 21 May 2014 23:23:51 +0000 (00:23 +0100)
13 files changed:
Documentation/devicetree/bindings/sound/fsl-sai.txt
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_sai.h
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_spdif.h
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/fsl_ssi.h
sound/soc/fsl/fsl_ssi_dbg.c [new file with mode: 0644]
sound/soc/fsl/imx-pcm-dma.c
sound/soc/soc-devres.c

index 98611a6..0f4e238 100644 (file)
@@ -7,10 +7,11 @@ codec/DSP interfaces.
 
 
 Required properties:
-- compatible: Compatible list, contains "fsl,vf610-sai".
+- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
 - reg: Offset and length of the register set for the device.
 - clocks: Must contain an entry for each entry in clock-names.
-- clock-names : Must include the "sai" entry.
+- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
+  "mclk3" for bit clock and frame clock providing.
 - dmas : Generic dma devicetree binding as described in
   Documentation/devicetree/bindings/dma/dma.txt.
 - dma-names : Two dmas have to be defined, "tx" and "rx".
@@ -30,8 +31,10 @@ sai2: sai@40031000 {
              reg = <0x40031000 0x1000>;
              pinctrl-names = "default";
              pinctrl-0 = <&pinctrl_sai2_1>;
-             clocks = <&clks VF610_CLK_SAI2>;
-             clock-names = "sai";
+             clocks = <&clks VF610_CLK_PLATFORM_BUS>,
+                    <&clks VF610_CLK_SAI2>,
+                    <&clks 0>, <&clks 0>;
+             clock-names = "bus", "mclk1", "mclk2", "mclk3";
              dma-names = "tx", "rx";
              dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
                   <&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
index f4069d0..d262ec0 100644 (file)
@@ -1,30 +1,77 @@
+menu "SoC Audio for Freescale CPUs"
+
+comment "Common SoC Audio options for Freescale CPUs:"
+
 config SND_SOC_FSL_SAI
-       tristate
+       tristate "Synchronous Audio Interface (SAI) module support"
        select REGMAP_MMIO
        select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say Y if you want to add Synchronous Audio Interface (SAI)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_SSI
-       tristate
+       tristate "Synchronous Serial Interface module support"
+       select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+       select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+       help
+         Say Y if you want to add Synchronous Serial Interface (SSI)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_SPDIF
-       tristate
+       tristate "Sony/Philips Digital Interface module support"
        select REGMAP_MMIO
+       select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
+       select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+       help
+         Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
+         support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_ESAI
-       tristate
+       tristate "Enhanced Serial Audio Interface (ESAI) module support"
        select REGMAP_MMIO
        select SND_SOC_FSL_UTILS
+       help
+         Say Y if you want to add Enhanced Synchronous Audio Interface
+         (ESAI) support for the Freescale CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
 
 config SND_SOC_FSL_UTILS
        tristate
 
-menuconfig SND_POWERPC_SOC
+config SND_SOC_IMX_PCM_DMA
+       tristate
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_IMX_AUDMUX
+       tristate "Digital Audio Mux module support"
+       help
+         Say Y if you want to add Digital Audio Mux (AUDMUX) support
+         for the ARM i.MX CPUs.
+         This option is only useful for out-of-tree drivers since
+         in-tree drivers select it automatically.
+
+config SND_POWERPC_SOC
        tristate "SoC Audio for Freescale PowerPC CPUs"
        depends on FSL_SOC || PPC_MPC52xx
        help
          Say Y or M if you want to add support for codecs attached to
          the PowerPC CPUs.
 
+config SND_IMX_SOC
+       tristate "SoC Audio for Freescale i.MX CPUs"
+       depends on ARCH_MXC || COMPILE_TEST
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the i.MX CPUs.
+
 if SND_POWERPC_SOC
 
 config SND_MPC52xx_DMA
@@ -33,6 +80,8 @@ config SND_MPC52xx_DMA
 config SND_SOC_POWERPC_DMA
        tristate
 
+comment "SoC Audio support for Freescale PPC boards:"
+
 config SND_SOC_MPC8610_HPCD
        tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
        # I2C is necessary for the CS4270 driver
@@ -110,13 +159,6 @@ config SND_MPC52xx_SOC_EFIKA
 
 endif # SND_POWERPC_SOC
 
-menuconfig SND_IMX_SOC
-       tristate "SoC Audio for Freescale i.MX CPUs"
-       depends on ARCH_MXC || COMPILE_TEST
-       help
-         Say Y or M if you want to add support for codecs attached to
-         the i.MX CPUs.
-
 if SND_IMX_SOC
 
 config SND_SOC_IMX_SSI
@@ -127,12 +169,7 @@ config SND_SOC_IMX_PCM_FIQ
        tristate
        select FIQ
 
-config SND_SOC_IMX_PCM_DMA
-       tristate
-       select SND_SOC_GENERIC_DMAENGINE_PCM
-
-config SND_SOC_IMX_AUDMUX
-       tristate
+comment "SoC Audio support for Freescale i.MX boards:"
 
 config SND_MXC_SOC_WM1133_EV1
        tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
@@ -225,3 +262,5 @@ config SND_SOC_IMX_MC13783
        select SND_SOC_IMX_PCM_DMA
 
 endif # SND_IMX_SOC
+
+endmenu
index b12ad4b..db254e3 100644 (file)
@@ -12,7 +12,8 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale SSI/DMA/SAI/SPDIF Support
 snd-soc-fsl-sai-objs := fsl_sai.o
-snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-ssi-y := fsl_ssi.o
+snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.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
index 496ce2e..d719caf 100644 (file)
@@ -39,6 +39,8 @@
  * @fifo_depth: depth of tx/rx FIFO
  * @slot_width: width of each DAI slot
  * @hck_rate: clock rate of desired HCKx clock
+ * @sck_rate: clock rate of desired SCKx clock
+ * @hck_dir: the direction of HCKx pads
  * @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
@@ -55,6 +57,8 @@ struct fsl_esai {
        u32 fifo_depth;
        u32 slot_width;
        u32 hck_rate[2];
+       u32 sck_rate[2];
+       bool hck_dir[2];
        bool sck_div[2];
        bool slave_mode;
        bool synchronous;
@@ -209,8 +213,13 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        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;
+       u32 ratio, ecr = 0;
        unsigned long clk_rate;
+       int ret;
+
+       /* Bypass divider settings if the requirement doesn't change */
+       if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx])
+               return 0;
 
        /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
        esai_priv->sck_div[tx] = true;
@@ -277,6 +286,7 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        esai_priv->sck_div[tx] = false;
 
 out:
+       esai_priv->hck_dir[tx] = dir;
        esai_priv->hck_rate[tx] = freq;
 
        regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
@@ -294,9 +304,10 @@ 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;
+       int ret;
 
-       /* Don't apply for fully slave mode*/
-       if (esai_priv->slave_mode)
+       /* Don't apply for fully slave mode or unchanged bclk */
+       if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq)
                return 0;
 
        if (ratio * freq > hck_rate)
@@ -319,8 +330,15 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
                return -EINVAL;
        }
 
-       return fsl_esai_divisor_cal(dai, tx, ratio, true,
+       ret = fsl_esai_divisor_cal(dai, tx, ratio, true,
                        esai_priv->sck_div[tx] ? 0 : ratio);
+       if (ret)
+               return ret;
+
+       /* Save current bclk rate */
+       esai_priv->sck_rate[tx] = freq;
+
+       return 0;
 }
 
 static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
@@ -439,8 +457,8 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 static int fsl_esai_startup(struct snd_pcm_substream *substream,
                            struct snd_soc_dai *dai)
 {
-       int ret;
        struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
+       int ret;
 
        /*
         * Some platforms might use the same bit to gate all three or two of
@@ -492,7 +510,8 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        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;
+       u32 bclk, mask, val;
+       int ret;
 
        bclk = params_rate(params) * esai_priv->slot_width * 2;
 
@@ -822,6 +841,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
 
 static const struct of_device_id fsl_esai_dt_ids[] = {
        { .compatible = "fsl,imx35-esai", },
+       { .compatible = "fsl,vf610-esai", },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
index 56da8c8..c5a0e8a 100644 (file)
@@ -22,6 +22,7 @@
 #include <sound/pcm_params.h>
 
 #include "fsl_sai.h"
+#include "imx-pcm.h"
 
 #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
                       FSL_SAI_CSR_FEIE)
@@ -30,78 +31,96 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid)
 {
        struct fsl_sai *sai = (struct fsl_sai *)devid;
        struct device *dev = &sai->pdev->dev;
-       u32 xcsr, mask;
+       u32 flags, xcsr, mask;
+       bool irq_none = true;
 
-       /* Only handle those what we enabled */
+       /*
+        * Both IRQ status bits and IRQ mask bits are in the xCSR but
+        * different shifts. And we here create a mask only for those
+        * IRQs that we activated.
+        */
        mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
 
        /* Tx IRQ */
        regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
-       xcsr &= mask;
+       flags = xcsr & mask;
+
+       if (flags)
+               irq_none = false;
+       else
+               goto irq_rx;
 
-       if (xcsr & FSL_SAI_CSR_WSF)
+       if (flags & FSL_SAI_CSR_WSF)
                dev_dbg(dev, "isr: Start of Tx word detected\n");
 
-       if (xcsr & FSL_SAI_CSR_SEF)
+       if (flags & FSL_SAI_CSR_SEF)
                dev_warn(dev, "isr: Tx Frame sync error detected\n");
 
-       if (xcsr & FSL_SAI_CSR_FEF) {
+       if (flags & FSL_SAI_CSR_FEF) {
                dev_warn(dev, "isr: Transmit underrun detected\n");
                /* FIFO reset for safety */
                xcsr |= FSL_SAI_CSR_FR;
        }
 
-       if (xcsr & FSL_SAI_CSR_FWF)
+       if (flags & FSL_SAI_CSR_FWF)
                dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
 
-       if (xcsr & FSL_SAI_CSR_FRF)
+       if (flags & FSL_SAI_CSR_FRF)
                dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
 
-       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
-                          FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+       flags &= FSL_SAI_CSR_xF_W_MASK;
+       xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+       if (flags)
+               regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
 
+irq_rx:
        /* Rx IRQ */
        regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
-       xcsr &= mask;
+       flags = xcsr & mask;
 
-       if (xcsr & FSL_SAI_CSR_WSF)
+       if (flags)
+               irq_none = false;
+       else
+               goto out;
+
+       if (flags & FSL_SAI_CSR_WSF)
                dev_dbg(dev, "isr: Start of Rx word detected\n");
 
-       if (xcsr & FSL_SAI_CSR_SEF)
+       if (flags & FSL_SAI_CSR_SEF)
                dev_warn(dev, "isr: Rx Frame sync error detected\n");
 
-       if (xcsr & FSL_SAI_CSR_FEF) {
+       if (flags & FSL_SAI_CSR_FEF) {
                dev_warn(dev, "isr: Receive overflow detected\n");
                /* FIFO reset for safety */
                xcsr |= FSL_SAI_CSR_FR;
        }
 
-       if (xcsr & FSL_SAI_CSR_FWF)
+       if (flags & FSL_SAI_CSR_FWF)
                dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
 
-       if (xcsr & FSL_SAI_CSR_FRF)
+       if (flags & FSL_SAI_CSR_FRF)
                dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
 
-       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
-                          FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
+       flags &= FSL_SAI_CSR_xF_W_MASK;
+       xcsr &= ~FSL_SAI_CSR_xF_MASK;
+
+       if (flags)
+               regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
 
-       return IRQ_HANDLED;
+out:
+       if (irq_none)
+               return IRQ_NONE;
+       else
+               return IRQ_HANDLED;
 }
 
 static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
                int clk_id, unsigned int freq, int fsl_dir)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr2, reg_cr2;
-
-       if (fsl_dir == FSL_FMT_TRANSMITTER)
-               reg_cr2 = FSL_SAI_TCR2;
-       else
-               reg_cr2 = FSL_SAI_RCR2;
-
-       regmap_read(sai->regmap, reg_cr2, &val_cr2);
-
-       val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
+       bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+       u32 val_cr2 = 0;
 
        switch (clk_id) {
        case FSL_SAI_CLK_BUS:
@@ -120,7 +139,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
-       regmap_write(sai->regmap, reg_cr2, val_cr2);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+                          FSL_SAI_CR2_MSEL_MASK, val_cr2);
 
        return 0;
 }
@@ -152,22 +172,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                                unsigned int fmt, int fsl_dir)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
-
-       if (fsl_dir == FSL_FMT_TRANSMITTER) {
-               reg_cr2 = FSL_SAI_TCR2;
-               reg_cr4 = FSL_SAI_TCR4;
-       } else {
-               reg_cr2 = FSL_SAI_RCR2;
-               reg_cr4 = FSL_SAI_RCR4;
-       }
+       bool tx = fsl_dir == FSL_FMT_TRANSMITTER;
+       u32 val_cr2 = 0, val_cr4 = 0;
 
-       regmap_read(sai->regmap, reg_cr2, &val_cr2);
-       regmap_read(sai->regmap, reg_cr4, &val_cr4);
-
-       if (sai->big_endian_data)
-               val_cr4 &= ~FSL_SAI_CR4_MF;
-       else
+       if (!sai->big_endian_data)
                val_cr4 |= FSL_SAI_CR4_MF;
 
        /* DAI mode */
@@ -188,7 +196,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * frame sync asserts with the first bit of the frame.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
                break;
        case SND_SOC_DAIFMT_DSP_A:
                /*
@@ -198,7 +205,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * data word.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~FSL_SAI_CR4_FSP;
                val_cr4 |= FSL_SAI_CR4_FSE;
                sai->is_dsp_mode = true;
                break;
@@ -208,7 +214,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                 * frame sync asserts with the first bit of the frame.
                 */
                val_cr2 |= FSL_SAI_CR2_BCP;
-               val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
                sai->is_dsp_mode = true;
                break;
        case SND_SOC_DAIFMT_RIGHT_J:
@@ -246,23 +251,22 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
                val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBM_CFM:
-               val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
-               val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBS_CFM:
                val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
-               val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
                break;
        case SND_SOC_DAIFMT_CBM_CFS:
-               val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
                val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
                break;
        default:
                return -EINVAL;
        }
 
-       regmap_write(sai->regmap, reg_cr2, val_cr2);
-       regmap_write(sai->regmap, reg_cr4, val_cr4);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx),
+                          FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR, val_cr2);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+                          FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
+                          FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR, val_cr4);
 
        return 0;
 }
@@ -289,29 +293,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        unsigned int channels = params_channels(params);
        u32 word_width = snd_pcm_format_width(params_format(params));
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               reg_cr4 = FSL_SAI_TCR4;
-               reg_cr5 = FSL_SAI_TCR5;
-               reg_mr = FSL_SAI_TMR;
-       } else {
-               reg_cr4 = FSL_SAI_RCR4;
-               reg_cr5 = FSL_SAI_RCR5;
-               reg_mr = FSL_SAI_RMR;
-       }
-
-       regmap_read(sai->regmap, reg_cr4, &val_cr4);
-       regmap_read(sai->regmap, reg_cr4, &val_cr5);
-
-       val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
-       val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
-
-       val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
-       val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
-       val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
+       u32 val_cr4 = 0, val_cr5 = 0;
 
        if (!sai->is_dsp_mode)
                val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
@@ -319,18 +304,20 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
        val_cr5 |= FSL_SAI_CR5_WNW(word_width);
        val_cr5 |= FSL_SAI_CR5_W0W(word_width);
 
-       val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
        if (sai->big_endian_data)
                val_cr5 |= FSL_SAI_CR5_FBT(0);
        else
                val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
 
        val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
-       val_mr = ~0UL - ((1 << channels) - 1);
 
-       regmap_write(sai->regmap, reg_cr4, val_cr4);
-       regmap_write(sai->regmap, reg_cr5, val_cr5);
-       regmap_write(sai->regmap, reg_mr, val_mr);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
+                          FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+                          val_cr4);
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx),
+                          FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+                          FSL_SAI_CR5_FBT_MASK, val_cr5);
+       regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1));
 
        return 0;
 }
@@ -339,6 +326,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
        u32 tcsr, rcsr;
 
        /*
@@ -353,14 +341,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
        regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
        regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               tcsr |= FSL_SAI_CSR_FRDE;
-               rcsr &= ~FSL_SAI_CSR_FRDE;
-       } else {
-               rcsr |= FSL_SAI_CSR_FRDE;
-               tcsr &= ~FSL_SAI_CSR_FRDE;
-       }
-
        /*
         * It is recommended that the transmitter is the last enabled
         * and the first disabled.
@@ -369,22 +349,33 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               tcsr |= FSL_SAI_CSR_TERE;
-               rcsr |= FSL_SAI_CSR_TERE;
+               if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) {
+                       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                                          FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+                       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                                          FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+               }
 
-               regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
-               regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
-                       tcsr &= ~FSL_SAI_CSR_TERE;
-                       rcsr &= ~FSL_SAI_CSR_TERE;
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_FRDE, 0);
+               regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+                                  FSL_SAI_CSR_xIE_MASK, 0);
+
+               /* Check if the opposite FRDE is also disabled */
+               if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) {
+                       regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+                                          FSL_SAI_CSR_TERE, 0);
+                       regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+                                          FSL_SAI_CSR_TERE, 0);
                }
-
-               regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
-               regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
                break;
        default:
                return -EINVAL;
@@ -397,14 +388,17 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 reg;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       struct device *dev = &sai->pdev->dev;
+       int ret;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               reg = FSL_SAI_TCR3;
-       else
-               reg = FSL_SAI_RCR3;
+       ret = clk_prepare_enable(sai->bus_clk);
+       if (ret) {
+               dev_err(dev, "failed to enable bus clock: %d\n", ret);
+               return ret;
+       }
 
-       regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE,
                           FSL_SAI_CR3_TRCE);
 
        return 0;
@@ -414,15 +408,11 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
                struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
-       u32 reg;
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               reg = FSL_SAI_TCR3;
-       else
-               reg = FSL_SAI_RCR3;
+       regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, 0);
 
-       regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
-                          ~FSL_SAI_CR3_TRCE);
+       clk_disable_unprepare(sai->bus_clk);
 }
 
 static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
@@ -438,8 +428,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
 {
        struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
 
-       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS);
-       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS);
+       regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
+       regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
        regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
                           FSL_SAI_MAXBURST_TX * 2);
        regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
@@ -555,7 +545,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
        struct fsl_sai *sai;
        struct resource *res;
        void __iomem *base;
-       int irq, ret;
+       char tmp[8];
+       int irq, ret, i;
 
        sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
        if (!sai)
@@ -563,6 +554,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
        sai->pdev = pdev;
 
+       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+               sai->sai_on_imx = true;
+
        sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
        if (sai->big_endian_regs)
                fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
@@ -575,12 +569,35 @@ static int fsl_sai_probe(struct platform_device *pdev)
                return PTR_ERR(base);
 
        sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
-                       "sai", base, &fsl_sai_regmap_config);
+                       "bus", base, &fsl_sai_regmap_config);
+
+       /* Compatible with old DTB cases */
+       if (IS_ERR(sai->regmap))
+               sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                               "sai", base, &fsl_sai_regmap_config);
        if (IS_ERR(sai->regmap)) {
                dev_err(&pdev->dev, "regmap init failed\n");
                return PTR_ERR(sai->regmap);
        }
 
+       /* No error out for old DTB cases but only mark the clock NULL */
+       sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(sai->bus_clk)) {
+               dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
+                               PTR_ERR(sai->bus_clk));
+               sai->bus_clk = NULL;
+       }
+
+       for (i = 0; i < FSL_SAI_MCLK_MAX; i++) {
+               sprintf(tmp, "mclk%d", i + 1);
+               sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
+               if (IS_ERR(sai->mclk_clk[i])) {
+                       dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
+                                       i + 1, PTR_ERR(sai->mclk_clk[i]));
+                       sai->mclk_clk[i] = NULL;
+               }
+       }
+
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
@@ -605,12 +622,16 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
-                       SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+       if (sai->sai_on_imx)
+               return imx_pcm_dma_init(pdev);
+       else
+               return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                               SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
 }
 
 static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,vf610-sai", },
+       { .compatible = "fsl,imx6sx-sai", },
        { /* sentinel */ }
 };
 
index a264185..0e6c9f5 100644 (file)
 #define FSL_SAI_RFR    0xc0 /* SAI Receive FIFO */
 #define FSL_SAI_RMR    0xe0 /* SAI Receive Mask */
 
+#define FSL_SAI_xCSR(tx)       (tx ? FSL_SAI_TCSR : FSL_SAI_RCSR)
+#define FSL_SAI_xCR1(tx)       (tx ? FSL_SAI_TCR1 : FSL_SAI_RCR1)
+#define FSL_SAI_xCR2(tx)       (tx ? FSL_SAI_TCR2 : FSL_SAI_RCR2)
+#define FSL_SAI_xCR3(tx)       (tx ? FSL_SAI_TCR3 : FSL_SAI_RCR3)
+#define FSL_SAI_xCR4(tx)       (tx ? FSL_SAI_TCR4 : FSL_SAI_RCR4)
+#define FSL_SAI_xCR5(tx)       (tx ? FSL_SAI_TCR5 : FSL_SAI_RCR5)
+#define FSL_SAI_xDR(tx)                (tx ? FSL_SAI_TDR : FSL_SAI_RDR)
+#define FSL_SAI_xFR(tx)                (tx ? FSL_SAI_TFR : FSL_SAI_RFR)
+#define FSL_SAI_xMR(tx)                (tx ? FSL_SAI_TMR : FSL_SAI_RMR)
+
 /* SAI Transmit/Recieve Control Register */
 #define FSL_SAI_CSR_TERE       BIT(31)
 #define FSL_SAI_CSR_FR         BIT(25)
@@ -48,6 +58,7 @@
 #define FSL_SAI_CSR_FWF                BIT(17)
 #define FSL_SAI_CSR_FRF                BIT(16)
 #define FSL_SAI_CSR_xIE_SHIFT  8
+#define FSL_SAI_CSR_xIE_MASK   (0x1f << FSL_SAI_CSR_xIE_SHIFT)
 #define FSL_SAI_CSR_WSIE       BIT(12)
 #define FSL_SAI_CSR_SEIE       BIT(11)
 #define FSL_SAI_CSR_FEIE       BIT(10)
 #define FSL_SAI_CLK_MAST2      2
 #define FSL_SAI_CLK_MAST3      3
 
+#define FSL_SAI_MCLK_MAX       3
+
 /* SAI data transfer numbers per DMA request */
 #define FSL_SAI_MAXBURST_TX 6
 #define FSL_SAI_MAXBURST_RX 6
 struct fsl_sai {
        struct platform_device *pdev;
        struct regmap *regmap;
+       struct clk *bus_clk;
+       struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
 
        bool big_endian_regs;
        bool big_endian_data;
        bool is_dsp_mode;
+       bool sai_on_imx;
 
        struct snd_dmaengine_dai_dma_data dma_params_rx;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
index 6452ca8..b912d45 100644 (file)
  * kind, whether express or implied.
  */
 
-#include <linux/module.h>
+#include <linux/bitrev.h>
 #include <linux/clk.h>
 #include <linux/clk-private.h>
-#include <linux/bitrev.h>
-#include <linux/regmap.h>
+#include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
+#include <linux/regmap.h>
 
 #include <sound/asoundef.h>
-#include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
 
 #include "fsl_spdif.h"
 #include "imx-pcm.h"
@@ -69,17 +69,42 @@ struct spdif_mixer_control {
        u32 ready_buf;
 };
 
+/**
+ * fsl_spdif_priv: Freescale SPDIF private data
+ *
+ * @fsl_spdif_control: SPDIF control data
+ * @cpu_dai_drv: cpu dai driver
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @dpll_locked: dpll lock flag
+ * @txrate: the best rates for playback
+ * @txclk_df: STC_TXCLK_DF dividers value for playback
+ * @sysclk_df: STC_SYSCLK_DF dividers value for playback
+ * @txclk_src: STC_TXCLK_SRC values for playback
+ * @rxclk_src: SRPC_CLKSRC_SEL values for capture
+ * @txclk: tx clock sources for playback
+ * @rxclk: rx clock sources for capture
+ * @coreclk: core clock for register access via DMA
+ * @sysclk: system clock for rx clock rate measurement
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @dma_params_rx: DMA parameters for receive channel
+ * @name: driver name
+ */
 struct fsl_spdif_priv {
        struct spdif_mixer_control fsl_spdif_control;
        struct snd_soc_dai_driver cpu_dai_drv;
        struct platform_device *pdev;
        struct regmap *regmap;
        bool dpll_locked;
-       u8 txclk_div[SPDIF_TXRATE_MAX];
+       u16 txrate[SPDIF_TXRATE_MAX];
+       u8 txclk_df[SPDIF_TXRATE_MAX];
+       u8 sysclk_df[SPDIF_TXRATE_MAX];
        u8 txclk_src[SPDIF_TXRATE_MAX];
        u8 rxclk_src;
        struct clk *txclk[SPDIF_TXRATE_MAX];
        struct clk *rxclk;
+       struct clk *coreclk;
+       struct clk *sysclk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
 
@@ -349,7 +374,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
        struct platform_device *pdev = spdif_priv->pdev;
        unsigned long csfs = 0;
        u32 stc, mask, rate;
-       u8 clk, div;
+       u8 clk, txclk_df, sysclk_df;
        int ret;
 
        switch (sample_rate) {
@@ -376,25 +401,31 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       div = spdif_priv->txclk_div[rate];
-       if (div == 0) {
-               dev_err(&pdev->dev, "the divisor can't be zero\n");
+       txclk_df = spdif_priv->txclk_df[rate];
+       if (txclk_df == 0) {
+               dev_err(&pdev->dev, "the txclk_df can't be zero\n");
                return -EINVAL;
        }
 
+       sysclk_df = spdif_priv->sysclk_df[rate];
+
+       /* Don't mess up the clocks from other modules */
+       if (clk != STC_TXCLK_SPDIF_ROOT)
+               goto clk_set_bypass;
+
        /*
-        * The S/PDIF block needs a clock of 64 * fs * div.  The S/PDIF block
-        * will divide by (div).  So request 64 * fs * (div+1) which will
-        * get rounded.
+        * The S/PDIF block needs a clock of 64 * fs * txclk_df.
+        * So request 64 * fs * (txclk_df + 1) to get rounded.
         */
-       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1));
        if (ret) {
                dev_err(&pdev->dev, "failed to set tx clock rate\n");
                return ret;
        }
 
+clk_set_bypass:
        dev_dbg(&pdev->dev, "expected clock rate = %d\n",
-                       (64 * sample_rate * div));
+                       (64 * sample_rate * txclk_df * sysclk_df));
        dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
                        clk_get_rate(spdif_priv->txclk[rate]));
 
@@ -402,11 +433,15 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
        spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
 
        /* select clock source and divisor */
-       stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
-       mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+       stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
+       mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
        regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
 
-       dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+       regmap_update_bits(regmap, REG_SPDIF_STC,
+                          STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
+
+       dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
+                       spdif_priv->txrate[rate], sample_rate);
 
        return 0;
 }
@@ -423,10 +458,16 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
 
        /* Reset module and interrupts only for first initialization */
        if (!cpu_dai->active) {
+               ret = clk_prepare_enable(spdif_priv->coreclk);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to enable core clock\n");
+                       return ret;
+               }
+
                ret = spdif_softreset(spdif_priv);
                if (ret) {
                        dev_err(&pdev->dev, "failed to soft reset\n");
-                       return ret;
+                       goto err;
                }
 
                /* Disable all the interrupts */
@@ -454,6 +495,11 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
        regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
 
        return 0;
+
+err:
+       clk_disable_unprepare(spdif_priv->coreclk);
+
+       return ret;
 }
 
 static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
@@ -484,6 +530,7 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
                spdif_intr_status_clear(spdif_priv);
                regmap_update_bits(regmap, REG_SPDIF_SCR,
                                SCR_LOW_POWER, SCR_LOW_POWER);
+               clk_disable_unprepare(spdif_priv->coreclk);
        }
 }
 
@@ -754,7 +801,7 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
        clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
        if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
                /* Get bus clock from system */
-               busclk_freq = clk_get_rate(spdif_priv->rxclk);
+               busclk_freq = clk_get_rate(spdif_priv->sysclk);
        }
 
        /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
@@ -997,43 +1044,61 @@ static struct regmap_config fsl_spdif_regmap_config = {
 
 static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
                                struct clk *clk, u64 savesub,
-                               enum spdif_txrate index)
+                               enum spdif_txrate index, bool round)
 {
        const u32 rate[] = { 32000, 44100, 48000 };
+       bool is_sysclk = clk == spdif_priv->sysclk;
        u64 rate_ideal, rate_actual, sub;
-       u32 div, arate;
-
-       for (div = 1; div <= 128; div++) {
-               rate_ideal = rate[index] * (div + 1) * 64;
-               rate_actual = clk_round_rate(clk, rate_ideal);
-
-               arate = rate_actual / 64;
-               arate /= div;
-
-               if (arate == rate[index]) {
-                       /* We are lucky */
-                       savesub = 0;
-                       spdif_priv->txclk_div[index] = div;
-                       break;
-               } else if (arate / rate[index] == 1) {
-                       /* A little bigger than expect */
-                       sub = (arate - rate[index]) * 100000;
-                       do_div(sub, rate[index]);
-                       if (sub < savesub) {
+       u32 sysclk_dfmin, sysclk_dfmax;
+       u32 txclk_df, sysclk_df, arate;
+
+       /* The sysclk has an extra divisor [2, 512] */
+       sysclk_dfmin = is_sysclk ? 2 : 1;
+       sysclk_dfmax = is_sysclk ? 512 : 1;
+
+       for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) {
+               for (txclk_df = 1; txclk_df <= 128; txclk_df++) {
+                       rate_ideal = rate[index] * (txclk_df + 1) * 64;
+                       if (round)
+                               rate_actual = clk_round_rate(clk, rate_ideal);
+                       else
+                               rate_actual = clk_get_rate(clk);
+
+                       arate = rate_actual / 64;
+                       arate /= txclk_df * sysclk_df;
+
+                       if (arate == rate[index]) {
+                               /* We are lucky */
+                               savesub = 0;
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
+                               goto out;
+                       } else if (arate / rate[index] == 1) {
+                               /* A little bigger than expect */
+                               sub = (arate - rate[index]) * 100000;
+                               do_div(sub, rate[index]);
+                               if (sub >= savesub)
+                                       continue;
                                savesub = sub;
-                               spdif_priv->txclk_div[index] = div;
-                       }
-               } else if (rate[index] / arate == 1) {
-                       /* A little smaller than expect */
-                       sub = (rate[index] - arate) * 100000;
-                       do_div(sub, rate[index]);
-                       if (sub < savesub) {
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
+                       } else if (rate[index] / arate == 1) {
+                               /* A little smaller than expect */
+                               sub = (rate[index] - arate) * 100000;
+                               do_div(sub, rate[index]);
+                               if (sub >= savesub)
+                                       continue;
                                savesub = sub;
-                               spdif_priv->txclk_div[index] = div;
+                               spdif_priv->txclk_df[index] = txclk_df;
+                               spdif_priv->sysclk_df[index] = sysclk_df;
+                               spdif_priv->txrate[index] = arate;
                        }
                }
        }
 
+out:
        return savesub;
 }
 
@@ -1058,7 +1123,8 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
                if (!clk_get_rate(clk))
                        continue;
 
-               ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+               ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index,
+                                            i == STC_TXCLK_SPDIF_ROOT);
                if (savesub == ret)
                        continue;
 
@@ -1073,8 +1139,13 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
 
        dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
                        spdif_priv->txclk_src[index], rate[index]);
-       dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
-                       spdif_priv->txclk_div[index], rate[index]);
+       dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
+                       spdif_priv->txclk_df[index], rate[index]);
+       if (spdif_priv->txclk[index] == spdif_priv->sysclk)
+               dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
+                               spdif_priv->sysclk_df[index], rate[index]);
+       dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
+                       rate[index], spdif_priv->txrate[index]);
 
        return 0;
 }
@@ -1134,6 +1205,20 @@ static int fsl_spdif_probe(struct platform_device *pdev)
                return ret;
        }
 
+       /* Get system clock for rx clock rate calculation */
+       spdif_priv->sysclk = devm_clk_get(&pdev->dev, "rxtx5");
+       if (IS_ERR(spdif_priv->sysclk)) {
+               dev_err(&pdev->dev, "no sys clock (rxtx5) in devicetree\n");
+               return PTR_ERR(spdif_priv->sysclk);
+       }
+
+       /* Get core clock for data register access via DMA */
+       spdif_priv->coreclk = devm_clk_get(&pdev->dev, "core");
+       if (IS_ERR(spdif_priv->coreclk)) {
+               dev_err(&pdev->dev, "no core clock in devicetree\n");
+               return PTR_ERR(spdif_priv->coreclk);
+       }
+
        /* Select clock source for rx/tx clock */
        spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
        if (IS_ERR(spdif_priv->rxclk)) {
@@ -1186,6 +1271,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
 
 static const struct of_device_id fsl_spdif_dt_ids[] = {
        { .compatible = "fsl,imx35-spdif", },
+       { .compatible = "fsl,vf610-spdif", },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
index 605a10b..16fde4b 100644 (file)
@@ -143,20 +143,22 @@ enum spdif_gainsel {
 #define INT_RXFIFO_FUL                 (1 << 0)
 
 /* SPDIF Clock register */
-#define STC_SYSCLK_DIV_OFFSET          11
-#define STC_SYSCLK_DIV_MASK            (0x1ff << STC_SYSCLK_DIV_OFFSET)
-#define STC_SYSCLK_DIV(x)              ((((x) - 1) << STC_SYSCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_SYSCLK_DF_OFFSET           11
+#define STC_SYSCLK_DF_MASK             (0x1ff << STC_SYSCLK_DF_OFFSET)
+#define STC_SYSCLK_DF(x)               ((((x) - 1) << STC_SYSCLK_DF_OFFSET) & STC_SYSCLK_DF_MASK)
 #define STC_TXCLK_SRC_OFFSET           8
 #define STC_TXCLK_SRC_MASK             (0x7 << STC_TXCLK_SRC_OFFSET)
 #define STC_TXCLK_SRC_SET(x)           ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
 #define STC_TXCLK_ALL_EN_OFFSET                7
 #define STC_TXCLK_ALL_EN_MASK          (1 << STC_TXCLK_ALL_EN_OFFSET)
 #define STC_TXCLK_ALL_EN               (1 << STC_TXCLK_ALL_EN_OFFSET)
-#define STC_TXCLK_DIV_OFFSET           0
-#define STC_TXCLK_DIV_MASK             (0x7ff << STC_TXCLK_DIV_OFFSET)
-#define STC_TXCLK_DIV(x)               ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_DF_OFFSET            0
+#define STC_TXCLK_DF_MASK              (0x7ff << STC_TXCLK_DF_OFFSET)
+#define STC_TXCLK_DF(x)                ((((x) - 1) << STC_TXCLK_DF_OFFSET) & STC_TXCLK_DF_MASK)
 #define STC_TXCLK_SRC_MAX              8
 
+#define STC_TXCLK_SPDIF_ROOT           1
+
 /* SPDIF tx rate */
 enum spdif_txrate {
        SPDIF_TXRATE_32000 = 0,
index 5428a1f..f233d91 100644 (file)
 #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>
 #include <linux/spinlock.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
@@ -113,8 +113,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
 #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,
@@ -146,7 +144,6 @@ struct fsl_ssi_rxtx_reg_val {
  * @cpu_dai: the CPU DAI for this device
  * @dev_attr: the sysfs device attribute structure
  * @stats: SSI statistics
- * @name: name for this device
  */
 struct fsl_ssi_private {
        struct ccsr_ssi __iomem *ssi;
@@ -155,15 +152,11 @@ struct fsl_ssi_private {
        unsigned int fifo_depth;
        struct snd_soc_dai_driver cpu_dai_drv;
        struct platform_device *pdev;
+       unsigned int dai_fmt;
 
        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;
        bool use_dual_fifo;
        u8 i2s_mode;
        spinlock_t baudclk_lock;
@@ -171,39 +164,11 @@ struct fsl_ssi_private {
        struct clk *clk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
-       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;
-               unsigned int tfrc;
-               unsigned int cmdau;
-               unsigned int cmddu;
-               unsigned int rxt;
-               unsigned int rdr1;
-               unsigned int rdr0;
-               unsigned int tde1;
-               unsigned int tde0;
-               unsigned int roe1;
-               unsigned int roe0;
-               unsigned int tue1;
-               unsigned int tue0;
-               unsigned int tfs;
-               unsigned int rfs;
-               unsigned int tls;
-               unsigned int rls;
-               unsigned int rff1;
-               unsigned int rff0;
-               unsigned int tfe1;
-               unsigned int tfe0;
-       } stats;
-       struct dentry *dbg_dir;
-       struct dentry *dbg_stats;
-
-       char name[1];
+       struct fsl_ssi_dbg dbg_stats;
 };
 
 static const struct of_device_id fsl_ssi_ids[] = {
@@ -215,6 +180,54 @@ static const struct of_device_id fsl_ssi_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
 
+static bool fsl_ssi_is_ac97(struct fsl_ssi_private *ssi_private)
+{
+       return !!(ssi_private->dai_fmt & SND_SOC_DAIFMT_AC97);
+}
+
+static bool fsl_ssi_on_imx(struct fsl_ssi_private *ssi_private)
+{
+       switch (ssi_private->hw_type) {
+       case FSL_SSI_MX21:
+       case FSL_SSI_MX35:
+       case FSL_SSI_MX51:
+               return true;
+       case FSL_SSI_MCP8610:
+               return false;
+       }
+
+       return false;
+}
+
+/*
+ * 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.
+ */
+static bool fsl_ssi_offline_config(struct fsl_ssi_private *ssi_private)
+{
+       switch (ssi_private->hw_type) {
+       case FSL_SSI_MCP8610:
+       case FSL_SSI_MX21:
+       case FSL_SSI_MX35:
+               return true;
+       case FSL_SSI_MX51:
+               return false;
+       }
+
+       return true;
+}
+
 /**
  * fsl_ssi_isr: SSI interrupt handler
  *
@@ -231,7 +244,6 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
 {
        struct fsl_ssi_private *ssi_private = dev_id;
        struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       irqreturn_t ret = IRQ_NONE;
        __be32 sisr;
        __be32 sisr2;
        __be32 sisr_write_mask = 0;
@@ -258,217 +270,18 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
           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) & FSLSSI_SISR_MASK;
-
-       if (sisr & CCSR_SSI_SISR_RFRC) {
-               ssi_private->stats.rfrc++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFRC) {
-               ssi_private->stats.tfrc++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_CMDAU) {
-               ssi_private->stats.cmdau++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_CMDDU) {
-               ssi_private->stats.cmddu++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RXT) {
-               ssi_private->stats.rxt++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RDR1) {
-               ssi_private->stats.rdr1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RDR0) {
-               ssi_private->stats.rdr0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TDE1) {
-               ssi_private->stats.tde1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TDE0) {
-               ssi_private->stats.tde0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_ROE1) {
-               ssi_private->stats.roe1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_ROE0) {
-               ssi_private->stats.roe0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TUE1) {
-               ssi_private->stats.tue1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TUE0) {
-               ssi_private->stats.tue0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFS) {
-               ssi_private->stats.tfs++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFS) {
-               ssi_private->stats.rfs++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TLS) {
-               ssi_private->stats.tls++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RLS) {
-               ssi_private->stats.rls++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFF1) {
-               ssi_private->stats.rff1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_RFF0) {
-               ssi_private->stats.rff0++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFE1) {
-               ssi_private->stats.tfe1++;
-               ret = IRQ_HANDLED;
-       }
-
-       if (sisr & CCSR_SSI_SISR_TFE0) {
-               ssi_private->stats.tfe0++;
-               ret = IRQ_HANDLED;
-       }
+       sisr = read_ssi(&ssi->sisr);
 
        sisr2 = sisr & sisr_write_mask;
        /* Clear the bits that we set */
        if (sisr2)
                write_ssi(sisr2, &ssi->sisr);
 
-       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);
+       fsl_ssi_dbg_isr(&ssi_private->dbg_stats, sisr);
 
-       return 0;
+       return IRQ_HANDLED;
 }
 
-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.
  */
@@ -490,6 +303,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
 }
 
 /*
+ * Calculate the bits that have to be disabled for the current stream that is
+ * getting disabled. This keeps the bits enabled that are necessary for the
+ * second stream to work if 'stream_active' is true.
+ *
+ * Detailed calculation:
+ * These are the values that need to be active after disabling. For non-active
+ * second stream, this is 0:
+ *     vals_stream * !!stream_active
+ *
+ * The following computes the overall differences between the setup for the
+ * to-disable stream and the active stream, a simple XOR:
+ *     vals_disable ^ (vals_stream * !!(stream_active))
+ *
+ * The full expression adds a mask on all values we care about
+ */
+#define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \
+       ((vals_disable) & \
+        ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active))))
+
+/*
  * Enable/Disable a ssi configuration. You have to pass either
  * ssi_private->rxtx_reg_val.rx or tx as vals parameter.
  */
@@ -501,6 +334,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
        u32 scr_val = read_ssi(&ssi->scr);
        int nr_active_streams = !!(scr_val & CCSR_SSI_SCR_TE) +
                                !!(scr_val & CCSR_SSI_SCR_RE);
+       int keep_active;
+
+       if (nr_active_streams - 1 > 0)
+               keep_active = 1;
+       else
+               keep_active = 0;
 
        /* Find the other direction values rx or tx which we do not want to
         * modify */
@@ -511,7 +350,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
 
        /* If vals should be disabled, start with disabling the unit */
        if (!enable) {
-               u32 scr = vals->scr & (vals->scr ^ avals->scr);
+               u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr,
+                               keep_active);
                write_ssi_mask(&ssi->scr, scr, 0);
        }
 
@@ -520,9 +360,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * 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 (fsl_ssi_offline_config(ssi_private)) {
                if ((enable && !nr_active_streams) ||
-                               (!enable && nr_active_streams == 1))
+                               (!enable && !keep_active))
                        fsl_ssi_rxtx_config(ssi_private, enable);
 
                goto config_done;
@@ -551,9 +391,12 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
                 */
 
                /* 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);
+               sier = fsl_ssi_disable_val(vals->sier, avals->sier,
+                               keep_active);
+               srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr,
+                               keep_active);
+               stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr,
+                               keep_active);
 
                write_ssi_mask(&ssi->srcr, srcr, 0);
                write_ssi_mask(&ssi->stcr, stcr, 0);
@@ -593,7 +436,7 @@ static void fsl_ssi_setup_reg_vals(struct fsl_ssi_private *ssi_private)
        reg->tx.stcr = CCSR_SSI_STCR_TFEN0;
        reg->tx.scr = 0;
 
-       if (!ssi_private->imx_ac97) {
+       if (!fsl_ssi_is_ac97(ssi_private)) {
                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;
@@ -642,96 +485,6 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
        write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
 }
 
-static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
-{
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       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
-               ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
-
-       /*
-        * Section 16.5 of the MPC8610 reference manual says that the SSI needs
-        * to be disabled before updating the registers we set here.
-        */
-       write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-
-       /*
-        * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
-        * enable the transmit and receive FIFO.
-        *
-        * FIXME: Little-endian samples require a different shift dir
-        */
-       write_ssi_mask(&ssi->scr,
-               CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
-               CCSR_SSI_SCR_TFR_CLK_DIS |
-               ssi_private->i2s_mode |
-               (synchronous ? CCSR_SSI_SCR_SYN : 0));
-
-       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);
-
-       /*
-        * The DC and PM bits are only used if the SSI is the clock master.
-        */
-
-       /*
-        * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
-        * use FIFO 1. We program the transmit water to signal a DMA transfer
-        * if there are only two (or fewer) elements left in the FIFO. Two
-        * elements equals one frame (left channel, right channel). This value,
-        * however, depends on the depth of the transmit buffer.
-        *
-        * We set the watermark on the same level as the DMA burstsize.  For
-        * fiq it is probably better to use the biggest possible watermark
-        * size.
-        */
-       if (ssi_private->use_dma)
-               wm = ssi_private->fifo_depth - 2;
-       else
-               wm = ssi_private->fifo_depth;
-
-       write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
-               CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
-               &ssi->sfcsr);
-
-       /*
-        * For ac97 interrupts are enabled with the startup of the substream
-        * because it is also running without an active substream. Normally SSI
-        * is only enabled when there is a substream.
-        */
-       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));
-       }
-
-       if (ssi_private->use_dual_fifo) {
-               write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1);
-               write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1);
-               write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN);
-       }
-
-       return 0;
-}
-
-
 /**
  * fsl_ssi_startup: create a new substream
  *
@@ -748,12 +501,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                snd_soc_dai_get_drvdata(rtd->cpu_dai);
        unsigned long flags;
 
-       /* First, we only do fsl_ssi_setup() when SSI is going to be active.
-        * Second, fsl_ssi_setup was already called by ac97_init earlier if
-        * the driver is in ac97 mode.
-        */
-       if (!dai->active && !ssi_private->imx_ac97) {
-               fsl_ssi_setup(ssi_private);
+       if (!dai->active && !fsl_ssi_is_ac97(ssi_private)) {
                spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
                ssi_private->baudclk_locked = false;
                spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
@@ -772,6 +520,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
 }
 
 /**
+ * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
+ *
+ * Note: This function can be only called when using SSI as DAI master
+ *
+ * Quick instruction for parameters:
+ * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
+ * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
+ */
+static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
+       u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
+       unsigned long flags, clkrate, baudrate, tmprate;
+       u64 sub, savesub = 100000;
+
+       /* Don't apply it to any non-baudclk circumstance */
+       if (IS_ERR(ssi_private->baudclk))
+               return -EINVAL;
+
+       /* It should be already enough to divide clock by setting pm alone */
+       psr = 0;
+       div2 = 0;
+
+       factor = (div2 + 1) * (7 * psr + 1) * 2;
+
+       for (i = 0; i < 255; i++) {
+               /* The bclk rate must be smaller than 1/5 sysclk rate */
+               if (factor * (i + 1) < 5)
+                       continue;
+
+               tmprate = freq * factor * (i + 2);
+               clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
+
+               do_div(clkrate, factor);
+               afreq = (u32)clkrate / (i + 1);
+
+               if (freq == afreq)
+                       sub = 0;
+               else if (freq / afreq == 1)
+                       sub = freq - afreq;
+               else if (afreq / freq == 1)
+                       sub = afreq - freq;
+               else
+                       continue;
+
+               /* Calculate the fraction */
+               sub *= 100000;
+               do_div(sub, freq);
+
+               if (sub < savesub) {
+                       baudrate = tmprate;
+                       savesub = sub;
+                       pm = i;
+               }
+
+               /* We are lucky */
+               if (savesub == 0)
+                       break;
+       }
+
+       /* No proper pm found if it is still remaining the initial value */
+       if (pm == 999) {
+               dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
+               return -EINVAL;
+       }
+
+       stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
+               (psr ? CCSR_SSI_SxCCR_PSR : 0);
+       mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 |
+               CCSR_SSI_SxCCR_PSR;
+
+       if (dir == SND_SOC_CLOCK_OUT || synchronous)
+               write_ssi_mask(&ssi->stccr, mask, stccr);
+       else
+               write_ssi_mask(&ssi->srccr, mask, stccr);
+
+       spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
+       if (!ssi_private->baudclk_locked) {
+               ret = clk_set_rate(ssi_private->baudclk, baudrate);
+               if (ret) {
+                       spin_unlock_irqrestore(&ssi_private->baudclk_lock,
+                                       flags);
+                       dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
+                       return -EINVAL;
+               }
+               ssi_private->baudclk_locked = true;
+       }
+       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+
+       return 0;
+}
+
+/**
  * fsl_ssi_hw_params - program the sample size
  *
  * Most of the SSI registers have been programmed in the startup function,
@@ -819,7 +663,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        else
                write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 
-       if (!ssi_private->imx_ac97)
+       if (!fsl_ssi_is_ac97(ssi_private))
                write_ssi_mask(&ssi->scr,
                                CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK,
                                channels == 1 ? 0 : ssi_private->i2s_mode);
@@ -835,9 +679,14 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
        struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
        u32 strcr = 0, stcr, srcr, scr, mask;
+       u8 wm;
+
+       ssi_private->dai_fmt = fmt;
+
+       fsl_ssi_setup_reg_vals(ssi_private);
 
        scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK);
-       scr |= CCSR_SSI_SCR_NET;
+       scr |= CCSR_SSI_SCR_SYNC_TX_FS;
 
        mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR |
                CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL |
@@ -845,19 +694,19 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
        stcr = read_ssi(&ssi->stcr) & ~mask;
        srcr = read_ssi(&ssi->srcr) & ~mask;
 
+       ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
                case SND_SOC_DAIFMT_CBS_CFS:
-                       ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
+                       ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
                        break;
                case SND_SOC_DAIFMT_CBM_CFM:
-                       ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+                       ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
                        break;
                default:
                        return -EINVAL;
                }
-               scr |= ssi_private->i2s_mode;
 
                /* Data on rising edge of bclk, frame low, 1clk before data */
                strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP |
@@ -877,9 +726,13 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP |
                        CCSR_SSI_STCR_TXBIT0;
                break;
+       case SND_SOC_DAIFMT_AC97:
+               ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_NORMAL;
+               break;
        default:
                return -EINVAL;
        }
+       scr |= ssi_private->i2s_mode;
 
        /* DAI clock inversion */
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -929,99 +782,37 @@ static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
        write_ssi(srcr, &ssi->srcr);
        write_ssi(scr, &ssi->scr);
 
-       return 0;
-}
-
-/**
- * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock
- *
- * Note: This function can be only called when using SSI as DAI master
- *
- * Quick instruction for parameters:
- * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels
- * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK.
- */
-static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-                                 int clk_id, unsigned int freq, int dir)
-{
-       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
-       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
-       u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
-       unsigned long flags, clkrate, baudrate, tmprate;
-       u64 sub, savesub = 100000;
-
-       /* Don't apply it to any non-baudclk circumstance */
-       if (IS_ERR(ssi_private->baudclk))
-               return -EINVAL;
-
-       /* It should be already enough to divide clock by setting pm alone */
-       psr = 0;
-       div2 = 0;
-
-       factor = (div2 + 1) * (7 * psr + 1) * 2;
-
-       for (i = 0; i < 255; i++) {
-               /* The bclk rate must be smaller than 1/5 sysclk rate */
-               if (factor * (i + 1) < 5)
-                       continue;
-
-               tmprate = freq * factor * (i + 2);
-               clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
-
-               do_div(clkrate, factor);
-               afreq = (u32)clkrate / (i + 1);
-
-               if (freq == afreq)
-                       sub = 0;
-               else if (freq / afreq == 1)
-                       sub = freq - afreq;
-               else if (afreq / freq == 1)
-                       sub = afreq - freq;
-               else
-                       continue;
-
-               /* Calculate the fraction */
-               sub *= 100000;
-               do_div(sub, freq);
-
-               if (sub < savesub) {
-                       baudrate = tmprate;
-                       savesub = sub;
-                       pm = i;
-               }
+       /*
+        * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+        * use FIFO 1. We program the transmit water to signal a DMA transfer
+        * if there are only two (or fewer) elements left in the FIFO. Two
+        * elements equals one frame (left channel, right channel). This value,
+        * however, depends on the depth of the transmit buffer.
+        *
+        * We set the watermark on the same level as the DMA burstsize.  For
+        * fiq it is probably better to use the biggest possible watermark
+        * size.
+        */
+       if (ssi_private->use_dma)
+               wm = ssi_private->fifo_depth - 2;
+       else
+               wm = ssi_private->fifo_depth;
 
-               /* We are lucky */
-               if (savesub == 0)
-                       break;
-       }
+       write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+                       CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
+                       &ssi->sfcsr);
 
-       /* No proper pm found if it is still remaining the initial value */
-       if (pm == 999) {
-               dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
-               return -EINVAL;
+       if (ssi_private->use_dual_fifo) {
+               write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1,
+                               CCSR_SSI_SRCR_RFEN1);
+               write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1,
+                               CCSR_SSI_STCR_TFEN1);
+               write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN,
+                               CCSR_SSI_SCR_TCH_EN);
        }
 
-       stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) |
-               (psr ? CCSR_SSI_SxCCR_PSR : 0);
-       mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR;
-
-       if (dir == SND_SOC_CLOCK_OUT || synchronous)
-               write_ssi_mask(&ssi->stccr, mask, stccr);
-       else
-               write_ssi_mask(&ssi->srccr, mask, stccr);
-
-       spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
-       if (!ssi_private->baudclk_locked) {
-               ret = clk_set_rate(ssi_private->baudclk, baudrate);
-               if (ret) {
-                       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
-                       dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
-                       return -EINVAL;
-               }
-               ssi_private->baudclk_locked = true;
-       }
-       spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
+       if (fmt & SND_SOC_DAIFMT_AC97)
+               fsl_ssi_setup_ac97(ssi_private);
 
        return 0;
 }
@@ -1097,7 +888,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
                else
                        fsl_ssi_rx_config(ssi_private, false);
 
-               if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
+               if (!fsl_ssi_is_ac97(ssi_private) && (read_ssi(&ssi->scr) &
                                        (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) {
                        spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
                        ssi_private->baudclk_locked = false;
@@ -1109,7 +900,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
                return -EINVAL;
        }
 
-       if (ssi_private->imx_ac97) {
+       if (fsl_ssi_is_ac97(ssi_private)) {
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
                else
@@ -1123,7 +914,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
 
-       if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
+       if (fsl_ssi_on_imx(ssi_private) && ssi_private->use_dma) {
                dai->playback_dma_data = &ssi_private->dma_params_tx;
                dai->capture_dma_data = &ssi_private->dma_params_rx;
        }
@@ -1184,11 +975,6 @@ static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
 
 static struct fsl_ssi_private *fsl_ac97_data;
 
-static void fsl_ssi_ac97_init(void)
-{
-       fsl_ssi_setup(fsl_ac97_data);
-}
-
 static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
                unsigned short val)
 {
@@ -1251,11 +1037,107 @@ static void make_lowercase(char *s)
        }
 }
 
+static int fsl_ssi_imx_probe(struct platform_device *pdev,
+               struct fsl_ssi_private *ssi_private, void __iomem *iomem)
+{
+       struct device_node *np = pdev->dev.of_node;
+       u32 dmas[4];
+       int ret;
+
+       ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(ssi_private->clk)) {
+               ret = PTR_ERR(ssi_private->clk);
+               dev_err(&pdev->dev, "could not get clock: %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(ssi_private->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+               return ret;
+       }
+
+       /* For those SLAVE implementations, we ingore non-baudclk cases
+        * and, instead, abandon MASTER mode that needs baud clock.
+        */
+       ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
+       if (IS_ERR(ssi_private->baudclk))
+               dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
+                        PTR_ERR(ssi_private->baudclk));
+       else
+               clk_prepare_enable(ssi_private->baudclk);
+
+       /*
+        * We have burstsize be "fifo_depth - 2" to match the SSI
+        * watermark setting in fsl_ssi_startup().
+        */
+       ssi_private->dma_params_tx.maxburst = ssi_private->fifo_depth - 2;
+       ssi_private->dma_params_rx.maxburst = ssi_private->fifo_depth - 2;
+       ssi_private->dma_params_tx.addr = ssi_private->ssi_phys +
+                       offsetof(struct ccsr_ssi, stx0);
+       ssi_private->dma_params_rx.addr = ssi_private->ssi_phys +
+                       offsetof(struct ccsr_ssi, srx0);
+
+       ret = !of_property_read_u32_array(np, "dmas", dmas, 4);
+       if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
+               ssi_private->use_dual_fifo = true;
+               /* When using dual fifo mode, we need to keep watermark
+                * as even numbers due to dma script limitation.
+                */
+               ssi_private->dma_params_tx.maxburst &= ~0x1;
+               ssi_private->dma_params_rx.maxburst &= ~0x1;
+       }
+
+       if (!ssi_private->use_dma) {
+
+               /*
+                * Some boards use an incompatible codec. To get it
+                * working, we are using imx-fiq-pcm-audio, that
+                * can handle those codecs. DMA is not possible in this
+                * situation.
+                */
+
+               ssi_private->fiq_params.irq = ssi_private->irq;
+               ssi_private->fiq_params.base = iomem;
+               ssi_private->fiq_params.dma_params_rx =
+                       &ssi_private->dma_params_rx;
+               ssi_private->fiq_params.dma_params_tx =
+                       &ssi_private->dma_params_tx;
+
+               ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+               if (ret)
+                       goto error_pcm;
+       } else {
+               ret = imx_pcm_dma_init(pdev);
+               if (ret)
+                       goto error_pcm;
+       }
+
+       return 0;
+
+error_pcm:
+       if (!IS_ERR(ssi_private->baudclk))
+               clk_disable_unprepare(ssi_private->baudclk);
+
+       clk_disable_unprepare(ssi_private->clk);
+
+       return ret;
+}
+
+static void fsl_ssi_imx_clean(struct platform_device *pdev,
+               struct fsl_ssi_private *ssi_private)
+{
+       if (!ssi_private->use_dma)
+               imx_pcm_fiq_exit(pdev);
+       if (!IS_ERR(ssi_private->baudclk))
+               clk_disable_unprepare(ssi_private->baudclk);
+       clk_disable_unprepare(ssi_private->clk);
+}
+
 static int fsl_ssi_probe(struct platform_device *pdev)
 {
        struct fsl_ssi_private *ssi_private;
        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;
@@ -1263,7 +1145,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        const uint32_t *iprop;
        struct resource res;
        char name[64];
-       bool shared;
        bool ac97 = false;
 
        /* SSIs that are not connected on the board should have a
@@ -1286,17 +1167,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (!strcmp(sprop, "ac97-slave"))
                ac97 = true;
 
-       /* The DAI name is the last part of the full name of the node. */
-       p = strrchr(np->full_name, '/') + 1;
-       ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
-                             GFP_KERNEL);
+       ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private),
+                       GFP_KERNEL);
        if (!ssi_private) {
                dev_err(&pdev->dev, "could not allocate DAI object\n");
                return -ENOMEM;
        }
 
-       strcpy(ssi_private->name, p);
-
        ssi_private->use_dma = !of_property_read_bool(np,
                        "fsl,fiq-stream-filter");
        ssi_private->hw_type = hw_type;
@@ -1306,7 +1183,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                                sizeof(fsl_ssi_ac97_dai));
 
                fsl_ac97_data = ssi_private;
-               ssi_private->imx_ac97 = true;
 
                snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
        } else {
@@ -1314,7 +1190,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
                       sizeof(fsl_ssi_dai_template));
        }
-       ssi_private->cpu_dai_drv.name = ssi_private->name;
+       ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev);
 
        /* Get the addresses and IRQ */
        ret = of_address_to_resource(np, 0, &res);
@@ -1353,177 +1229,43 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        ssi_private->baudclk_locked = false;
        spin_lock_init(&ssi_private->baudclk_lock);
 
-       /*
-        * 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], dmas[4];
-               ssi_private->ssi_on_imx = true;
+       dev_set_drvdata(&pdev->dev, ssi_private);
 
-               ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
-               if (IS_ERR(ssi_private->clk)) {
-                       ret = PTR_ERR(ssi_private->clk);
-                       dev_err(&pdev->dev, "could not get clock: %d\n", ret);
-                       goto error_irqmap;
-               }
-               ret = clk_prepare_enable(ssi_private->clk);
-               if (ret) {
-                       dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
-                               ret);
+       if (fsl_ssi_on_imx(ssi_private)) {
+               ret = fsl_ssi_imx_probe(pdev, ssi_private, ssi_private->ssi);
+               if (ret)
                        goto error_irqmap;
-               }
-
-               /* For those SLAVE implementations, we ingore non-baudclk cases
-                * and, instead, abandon MASTER mode that needs baud clock.
-                */
-               ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
-               if (IS_ERR(ssi_private->baudclk))
-                       dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
-                                PTR_ERR(ssi_private->baudclk));
-               else
-                       clk_prepare_enable(ssi_private->baudclk);
-
-               /*
-                * We have burstsize be "fifo_depth - 2" to match the SSI
-                * watermark setting in fsl_ssi_startup().
-                */
-               ssi_private->dma_params_tx.maxburst =
-                       ssi_private->fifo_depth - 2;
-               ssi_private->dma_params_rx.maxburst =
-                       ssi_private->fifo_depth - 2;
-               ssi_private->dma_params_tx.addr =
-                       ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
-               ssi_private->dma_params_rx.addr =
-                       ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
-               ssi_private->dma_params_tx.filter_data =
-                       &ssi_private->filter_data_tx;
-               ssi_private->dma_params_rx.filter_data =
-                       &ssi_private->filter_data_rx;
-               if (!of_property_read_bool(pdev->dev.of_node, "dmas") &&
-                               ssi_private->use_dma) {
-                       /*
-                        * FIXME: This is a temporary solution until all
-                        * necessary dma drivers support the generic dma
-                        * bindings.
-                        */
-                       ret = of_property_read_u32_array(pdev->dev.of_node,
-                                       "fsl,ssi-dma-events", dma_events, 2);
-                       if (ret && ssi_private->use_dma) {
-                               dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n");
-                               goto error_clk;
-                       }
-               }
-               /* Should this be merge with the above? */
-               if (!of_property_read_u32_array(pdev->dev.of_node, "dmas", dmas, 4)
-                               && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
-                       ssi_private->use_dual_fifo = true;
-                       /* When using dual fifo mode, we need to keep watermark
-                        * as even numbers due to dma script limitation.
-                        */
-                       ssi_private->dma_params_tx.maxburst &= ~0x1;
-                       ssi_private->dma_params_rx.maxburst &= ~0x1;
-               }
-
-               shared = of_device_is_compatible(of_get_parent(np),
-                           "fsl,spba-bus");
+       }
 
-               imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
-                       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);
+       ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
+                                        &ssi_private->cpu_dai_drv, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+               goto error_asoc_register;
        }
 
-       /*
-        * 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,
+                                       fsl_ssi_isr, 0, dev_name(&pdev->dev),
                                        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_clk;
+                       goto error_irq;
                }
        }
 
-       /* Register with ASoC */
-       dev_set_drvdata(&pdev->dev, ssi_private);
-
-       ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
-                                        &ssi_private->cpu_dai_drv, 1);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
-               goto error_dev;
-       }
-
-       ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev);
+       ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
        if (ret)
-               goto error_dbgfs;
-
-       if (ssi_private->ssi_on_imx) {
-               if (!ssi_private->use_dma) {
-
-                       /*
-                        * Some boards use an incompatible codec. To get it
-                        * working, we are using imx-fiq-pcm-audio, that
-                        * can handle those codecs. DMA is not possible in this
-                        * situation.
-                        */
-
-                       ssi_private->fiq_params.irq = ssi_private->irq;
-                       ssi_private->fiq_params.base = ssi_private->ssi;
-                       ssi_private->fiq_params.dma_params_rx =
-                               &ssi_private->dma_params_rx;
-                       ssi_private->fiq_params.dma_params_tx =
-                               &ssi_private->dma_params_tx;
-
-                       ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
-                       if (ret)
-                               goto error_pcm;
-               } else {
-                       ret = imx_pcm_dma_init(pdev);
-                       if (ret)
-                               goto error_pcm;
-               }
-       }
+               goto error_asoc_register;
 
        /*
         * If codec-handle property is missing from SSI node, we assume
         * that the machine driver uses new binding which does not require
         * SSI driver to trigger machine driver's probe.
         */
-       if (!of_get_property(np, "codec-handle", NULL)) {
-               ssi_private->new_binding = true;
+       if (!of_get_property(np, "codec-handle", NULL))
                goto done;
-       }
 
        /* Trigger the machine driver's probe function.  The platform driver
         * name of the machine driver is taken from /compatible property of the
@@ -1543,37 +1285,24 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (IS_ERR(ssi_private->pdev)) {
                ret = PTR_ERR(ssi_private->pdev);
                dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
-               goto error_dai;
+               goto error_sound_card;
        }
 
 done:
-       if (ssi_private->imx_ac97)
-               fsl_ssi_ac97_init();
-
        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_sound_card:
+       fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
 
-error_dbgfs:
+error_irq:
        snd_soc_unregister_component(&pdev->dev);
 
-error_dev:
-       device_remove_file(&pdev->dev, dev_attr);
-
-error_clk:
-       if (ssi_private->ssi_on_imx) {
-               if (!IS_ERR(ssi_private->baudclk))
-                       clk_disable_unprepare(ssi_private->baudclk);
-               clk_disable_unprepare(ssi_private->clk);
-       }
+error_asoc_register:
+       if (fsl_ssi_on_imx(ssi_private))
+               fsl_ssi_imx_clean(pdev, ssi_private);
 
 error_irqmap:
-       if (ssi_private->irq_stats)
+       if (ssi_private->use_dma)
                irq_dispose_mapping(ssi_private->irq);
 
        return ret;
@@ -1583,17 +1312,16 @@ 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);
+       fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
 
-       if (!ssi_private->new_binding)
+       if (ssi_private->pdev)
                platform_device_unregister(ssi_private->pdev);
        snd_soc_unregister_component(&pdev->dev);
-       if (ssi_private->ssi_on_imx) {
-               if (!IS_ERR(ssi_private->baudclk))
-                       clk_disable_unprepare(ssi_private->baudclk);
-               clk_disable_unprepare(ssi_private->clk);
-       }
-       if (ssi_private->irq_stats)
+
+       if (fsl_ssi_on_imx(ssi_private))
+               fsl_ssi_imx_clean(pdev, ssi_private);
+
+       if (ssi_private->use_dma)
                irq_dispose_mapping(ssi_private->irq);
 
        return 0;
index e6b6324..71c3e7e 100644 (file)
@@ -39,6 +39,7 @@ struct ccsr_ssi {
        __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */
 };
 
+#define CCSR_SSI_SCR_SYNC_TX_FS                0x00001000
 #define CCSR_SSI_SCR_RFR_CLK_DIS       0x00000800
 #define CCSR_SSI_SCR_TFR_CLK_DIS       0x00000400
 #define CCSR_SSI_SCR_TCH_EN            0x00000100
@@ -206,5 +207,64 @@ struct ccsr_ssi {
 #define CCSR_SSI_SACNT_FV              0x00000002
 #define CCSR_SSI_SACNT_AC97EN          0x00000001
 
-#endif
 
+struct device;
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+
+struct fsl_ssi_dbg {
+       struct dentry *dbg_dir;
+       struct dentry *dbg_stats;
+
+       struct {
+               unsigned int rfrc;
+               unsigned int tfrc;
+               unsigned int cmdau;
+               unsigned int cmddu;
+               unsigned int rxt;
+               unsigned int rdr1;
+               unsigned int rdr0;
+               unsigned int tde1;
+               unsigned int tde0;
+               unsigned int roe1;
+               unsigned int roe0;
+               unsigned int tue1;
+               unsigned int tue0;
+               unsigned int tfs;
+               unsigned int rfs;
+               unsigned int tls;
+               unsigned int rls;
+               unsigned int rff1;
+               unsigned int rff0;
+               unsigned int tfe1;
+               unsigned int tfe0;
+       } stats;
+};
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *ssi_dbg, u32 sisr);
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev);
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg);
+
+#else
+
+struct fsl_ssi_dbg {
+};
+
+static inline void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *stats, u32 sisr)
+{
+}
+
+static inline int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg,
+               struct device *dev)
+{
+       return 0;
+}
+
+static inline void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+}
+#endif  /* ! IS_ENABLED(CONFIG_DEBUG_FS) */
+
+#endif
diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c
new file mode 100644 (file)
index 0000000..5469ffb
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions
+ *
+ * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix
+ *
+ * Splitted from fsl_ssi.c
+ *
+ * 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/debugfs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include "fsl_ssi.h"
+
+void fsl_ssi_dbg_isr(struct fsl_ssi_dbg *dbg, u32 sisr)
+{
+       if (sisr & CCSR_SSI_SISR_RFRC)
+               dbg->stats.rfrc++;
+
+       if (sisr & CCSR_SSI_SISR_TFRC)
+               dbg->stats.tfrc++;
+
+       if (sisr & CCSR_SSI_SISR_CMDAU)
+               dbg->stats.cmdau++;
+
+       if (sisr & CCSR_SSI_SISR_CMDDU)
+               dbg->stats.cmddu++;
+
+       if (sisr & CCSR_SSI_SISR_RXT)
+               dbg->stats.rxt++;
+
+       if (sisr & CCSR_SSI_SISR_RDR1)
+               dbg->stats.rdr1++;
+
+       if (sisr & CCSR_SSI_SISR_RDR0)
+               dbg->stats.rdr0++;
+
+       if (sisr & CCSR_SSI_SISR_TDE1)
+               dbg->stats.tde1++;
+
+       if (sisr & CCSR_SSI_SISR_TDE0)
+               dbg->stats.tde0++;
+
+       if (sisr & CCSR_SSI_SISR_ROE1)
+               dbg->stats.roe1++;
+
+       if (sisr & CCSR_SSI_SISR_ROE0)
+               dbg->stats.roe0++;
+
+       if (sisr & CCSR_SSI_SISR_TUE1)
+               dbg->stats.tue1++;
+
+       if (sisr & CCSR_SSI_SISR_TUE0)
+               dbg->stats.tue0++;
+
+       if (sisr & CCSR_SSI_SISR_TFS)
+               dbg->stats.tfs++;
+
+       if (sisr & CCSR_SSI_SISR_RFS)
+               dbg->stats.rfs++;
+
+       if (sisr & CCSR_SSI_SISR_TLS)
+               dbg->stats.tls++;
+
+       if (sisr & CCSR_SSI_SISR_RLS)
+               dbg->stats.rls++;
+
+       if (sisr & CCSR_SSI_SISR_RFF1)
+               dbg->stats.rff1++;
+
+       if (sisr & CCSR_SSI_SISR_RFF0)
+               dbg->stats.rff0++;
+
+       if (sisr & CCSR_SSI_SISR_TFE1)
+               dbg->stats.tfe1++;
+
+       if (sisr & CCSR_SSI_SISR_TFE0)
+               dbg->stats.tfe0++;
+}
+
+/* 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 (CCSR_SSI_SIER_##flag) \
+                       seq_printf(s, #name "=%u\n", ssi_dbg->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_dbg *ssi_dbg = 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,
+};
+
+int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev)
+{
+       ssi_dbg->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
+       if (!ssi_dbg->dbg_dir)
+               return -ENOMEM;
+
+       ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO,
+                       ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops);
+       if (!ssi_dbg->dbg_stats) {
+               debugfs_remove(ssi_dbg->dbg_dir);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+void fsl_ssi_debugfs_remove(struct fsl_ssi_dbg *ssi_dbg)
+{
+       debugfs_remove(ssi_dbg->dbg_stats);
+       debugfs_remove(ssi_dbg->dbg_dir);
+}
index 2585ae4..0849b7b 100644 (file)
@@ -40,7 +40,6 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
                SNDRV_PCM_INFO_MMAP_VALID |
                SNDRV_PCM_INFO_PAUSE |
                SNDRV_PCM_INFO_RESUME,
-       .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
        .period_bytes_min = 128,
        .period_bytes_max = 65535, /* Limited by SDMA engine */
index e94aa02..057e5ef 100644 (file)
@@ -85,6 +85,7 @@ int devm_snd_soc_register_platform(struct device *dev,
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform);
 
 static void devm_card_release(struct device *dev, void *res)
 {