]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - sound/soc/fsl/fsl_ssi.c
Merge remote-tracking branches 'asoc/topic/devm', 'asoc/topic/fsl', 'asoc/topic/fsl...
[karo-tx-linux.git] / sound / soc / fsl / fsl_ssi.c
index 5428a1fda2603850349fa2fb11680df9b63386eb..f233d915b7e4ad8e45cb4a104aab2ba5c2087e8a 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.
  */
@@ -489,6 +302,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);
@@ -771,6 +519,102 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
        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;
+               }
+
+               /* 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
  *
@@ -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;