* 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.
+ *
+ *
+ * Some notes why imx-pcm-fiq is used instead of DMA on some boards:
+ *
+ * The i.MX SSI core has some nasty limitations in AC97 mode. While most
+ * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
+ * one FIFO which combines all valid receive slots. We cannot even select
+ * which slots we want to receive. The WM9712 with which this driver
+ * was developed with always sends GPIO status data in slot 12 which
+ * we receive in our (PCM-) data stream. The only chance we have is to
+ * manually skip this data in the FIQ handler. With sampling rates different
+ * from 48000Hz not every frame has valid receive data, so the ratio
+ * between pcm data and GPIO status data changes. Our FIQ handler is not
+ * able to handle this, hence this driver only works with 48000Hz sampling
+ * rate.
+ * Reading and writing AC97 registers is another challenge. The core
+ * provides us status bits when the read register is updated with *another*
+ * value. When we read the same register two times (and the register still
+ * contains the same value) these status bits are not set. We work
+ * around this by not polling these bits but only wait a fixed delay.
  */
 
 #include <linux/init.h>
 
        bool new_binding;
        bool ssi_on_imx;
+       bool use_dma;
        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;
 
        struct {
                unsigned int rfrc;
                 */
 
                /* Enable the interrupts and DMA requests */
-               write_ssi(SIER_FLAGS, &ssi->sier);
+               if (ssi_private->use_dma)
+                       write_ssi(SIER_FLAGS, &ssi->sier);
+               else
+                       write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN |
+                                       CCSR_SSI_SIER_RIE |
+                                       CCSR_SSI_SIER_RFF0_EN, &ssi->sier);
 
                /*
                 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
 
-       if (ssi_private->ssi_on_imx) {
+       if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
                dai->playback_dma_data = &ssi_private->dma_params_tx;
                dai->capture_dma_data = &ssi_private->dma_params_rx;
        }
 
        strcpy(ssi_private->name, p);
 
+       ssi_private->use_dma = !of_property_read_bool(np,
+                       "fsl,fiq-stream-filter");
+
        /* Initialize this copy of the CPU DAI driver structure */
        memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
               sizeof(fsl_ssi_dai_template));
                return -ENXIO;
        }
 
-       /* The 'name' should not have any slashes in it. */
-       ret = devm_request_irq(&pdev->dev, ssi_private->irq, fsl_ssi_isr, 0,
-                              ssi_private->name, ssi_private);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
-               goto error_irqmap;
+       if (ssi_private->use_dma) {
+               /* The 'name' should not have any slashes in it. */
+               ret = devm_request_irq(&pdev->dev, ssi_private->irq,
+                                       fsl_ssi_isr, 0, ssi_private->name,
+                                       ssi_private);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "could not claim irq %u\n",
+                                       ssi_private->irq);
+                       goto error_irqmap;
+               }
        }
 
        /* Are the RX and the TX clocks locked? */
                 */
                ret = of_property_read_u32_array(pdev->dev.of_node,
                                        "fsl,ssi-dma-events", dma_events, 2);
-               if (ret) {
+               if (ret && !ssi_private->use_dma) {
                        dev_err(&pdev->dev, "could not get dma events\n");
                        goto error_clk;
                }
        }
 
        if (ssi_private->ssi_on_imx) {
-               ret = imx_pcm_dma_init(pdev);
-               if (ret)
-                       goto error_dev;
+               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_dev;
+               } else {
+                       ret = imx_pcm_dma_init(pdev);
+                       if (ret)
+                               goto error_dev;
+               }
        }
 
        /*