//
 
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <sound/dmaengine_pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
        .pointer        = kmb_pcm_pointer,
 };
 
+static const struct snd_soc_component_driver kmb_component_dma = {
+       .name           = "kmb",
+};
+
+static int kmb_probe(struct snd_soc_dai *cpu_dai)
+{
+       struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+       if (kmb_i2s->use_pio)
+               return 0;
+
+       snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
+                                 &kmb_i2s->capture_dma_data);
+
+       return 0;
+}
+
+static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
+{
+       u32 dma_reg;
+
+       dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
+       /* Enable DMA handshake for stream */
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_reg |= I2S_DMAEN_TXBLOCK;
+       else
+               dma_reg |= I2S_DMAEN_RXBLOCK;
+
+       writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
+}
+
+static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
+{
+       u32 dma_reg;
+
+       dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
+       /* Disable DMA handshake for stream */
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dma_reg &= ~I2S_DMAEN_TXBLOCK;
+               writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
+       } else {
+               dma_reg &= ~I2S_DMAEN_RXBLOCK;
+               writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
+       }
+       writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
+}
+
 static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
                          struct snd_pcm_substream *substream)
 {
        else
                writel(1, kmb_i2s->i2s_base + IRER);
 
-       kmb_i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true);
+       if (kmb_i2s->use_pio)
+               kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
+                                   config->chan_nr, true);
+       else
+               kmb_i2s_enable_dma(kmb_i2s, substream->stream);
 
        if (kmb_i2s->clock_provider)
                writel(1, kmb_i2s->i2s_base + CER);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                kmb_i2s->active--;
-               kmb_i2s_stop(kmb_i2s, substream);
+               if (kmb_i2s->use_pio)
+                       kmb_i2s_stop(kmb_i2s, substream);
                break;
        default:
                return  -EINVAL;
                config->data_width = 16;
                kmb_i2s->ccr = 0x00;
                kmb_i2s->xfer_resolution = 0x02;
+               kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
                break;
        case SNDRV_PCM_FORMAT_S24_LE:
                config->data_width = 32;
                kmb_i2s->ccr = 0x14;
                kmb_i2s->xfer_resolution = 0x05;
+               kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
                break;
        case SNDRV_PCM_FORMAT_S32_LE:
                config->data_width = 32;
                kmb_i2s->ccr = 0x10;
                kmb_i2s->xfer_resolution = 0x05;
+               kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
                break;
        default:
                dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
        return 0;
 }
 
+static int kmb_dai_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *cpu_dai)
+{
+       struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+       struct snd_dmaengine_dai_dma_data *dma_data;
+
+       if (kmb_i2s->use_pio)
+               return 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_data = &kmb_i2s->play_dma_data;
+       else
+               dma_data = &kmb_i2s->capture_dma_data;
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
+
+       return 0;
+}
+
+static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *cpu_dai)
+{
+       struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
+       /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
+       if (kmb_i2s->use_pio)
+               kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               writel(0, kmb_i2s->i2s_base + ITER);
+       else
+               writel(0, kmb_i2s->i2s_base + IRER);
+
+       if (kmb_i2s->use_pio)
+               kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
+       else
+               kmb_i2s_disable_dma(kmb_i2s, substream->stream);
+
+       if (!kmb_i2s->active) {
+               writel(0, kmb_i2s->i2s_base + CER);
+               writel(0, kmb_i2s->i2s_base + IER);
+       }
+
+       return 0;
+}
+
 static struct snd_soc_dai_ops kmb_dai_ops = {
+       .startup        = kmb_dai_startup,
        .trigger        = kmb_dai_trigger,
        .hw_params      = kmb_dai_hw_params,
+       .hw_free        = kmb_dai_hw_free,
        .prepare        = kmb_dai_prepare,
        .set_fmt        = kmb_set_dai_fmt,
 };
                                    SNDRV_PCM_FMTBIT_S16_LE),
                },
                .ops = &kmb_dai_ops,
+               .probe = kmb_probe,
        },
 };
 
                                    SNDRV_PCM_FMTBIT_S16_LE),
                },
                .ops = &kmb_dai_ops,
+               .probe = kmb_probe,
        },
 };
 
 
 static int kmb_plat_dai_probe(struct platform_device *pdev)
 {
+       struct device_node *np = pdev->dev.of_node;
        struct snd_soc_dai_driver *kmb_i2s_dai;
        const struct of_device_id *match;
        struct device *dev = &pdev->dev;
        struct kmb_i2s_info *kmb_i2s;
+       struct resource *res;
        int ret, irq;
        u32 comp1_reg;
 
                return PTR_ERR(kmb_i2s->clk_i2s);
        }
 
-       kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0);
+       kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(kmb_i2s->i2s_base))
                return PTR_ERR(kmb_i2s->i2s_base);
 
 
        kmb_i2s->dev = &pdev->dev;
 
-       irq = platform_get_irq_optional(pdev, 0);
-       if (irq > 0) {
-               ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
-                                      pdev->name, kmb_i2s);
-               if (ret < 0) {
-                       dev_err(dev, "failed to request irq\n");
-                       return ret;
-               }
-       }
-
        comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
 
        kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
 
-       ret = devm_snd_soc_register_component(dev, &kmb_component,
-                                             kmb_i2s_dai, 1);
+       kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas"));
+
+       if (kmb_i2s->use_pio) {
+               irq = platform_get_irq_optional(pdev, 0);
+               if (irq > 0) {
+                       ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
+                                              pdev->name, kmb_i2s);
+                       if (ret < 0) {
+                               dev_err(dev, "failed to request irq\n");
+                               return ret;
+                       }
+               }
+               ret = devm_snd_soc_register_component(dev, &kmb_component,
+                                                     kmb_i2s_dai, 1);
+       } else {
+               kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
+               kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
+               ret = snd_dmaengine_pcm_register(&pdev->dev,
+                                                NULL, 0);
+               if (ret) {
+                       dev_err(&pdev->dev, "could not register dmaengine: %d\n",
+                               ret);
+                       return ret;
+               }
+               ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
+                                                     kmb_i2s_dai, 1);
+       }
+
        if (ret) {
                dev_err(dev, "not able to register dai\n");
                return ret;