#include <asm/mach-jz4740/gpio.h>
 #include <asm/cacheflush.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 
+#include <asm/mach-jz4740/dma.h>
 #include <asm/mach-jz4740/jz4740_mmc.h>
 
 #define JZ_REG_MMC_STRPCL      0x00
        int card_detect_irq;
 
        void __iomem *base;
+       struct resource *mem_res;
        struct mmc_request *req;
        struct mmc_command *cmd;
 
        struct timer_list timeout_timer;
        struct sg_mapping_iter miter;
        enum jz4740_mmc_state state;
+
+       /* DMA support */
+       struct dma_chan *dma_rx;
+       struct dma_chan *dma_tx;
+       bool use_dma;
+       int sg_len;
+
+/* The DMA trigger level is 8 words, that is to say, the DMA read
+ * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
+ * trigger is when data words in MSC_TXFIFO is < 8.
+ */
+#define JZ4740_MMC_FIFO_HALF_SIZE 8
 };
 
+/*----------------------------------------------------------------------------*/
+/* DMA infrastructure */
+
+static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
+{
+       if (!host->use_dma)
+               return;
+
+       dma_release_channel(host->dma_tx);
+       dma_release_channel(host->dma_rx);
+}
+
+static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
+{
+       dma_cap_mask_t mask;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       host->dma_tx = dma_request_channel(mask, NULL, host);
+       if (!host->dma_tx) {
+               dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
+               return -ENODEV;
+       }
+
+       host->dma_rx = dma_request_channel(mask, NULL, host);
+       if (!host->dma_rx) {
+               dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
+               goto free_master_write;
+       }
+
+       return 0;
+
+free_master_write:
+       dma_release_channel(host->dma_tx);
+       return -ENODEV;
+}
+
+static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data)
+{
+       return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+}
+
+static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
+                                struct mmc_data *data)
+{
+       struct dma_chan *chan;
+       enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
+
+       if (dir == DMA_TO_DEVICE)
+               chan = host->dma_tx;
+       else
+               chan = host->dma_rx;
+
+       dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+}
+
+static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
+                                        struct mmc_data *data)
+{
+       struct dma_chan *chan;
+       struct dma_async_tx_descriptor *desc;
+       struct dma_slave_config conf = {
+               .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+               .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+               .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
+               .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
+       };
+       enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
+
+       if (dir == DMA_TO_DEVICE) {
+               conf.direction = DMA_MEM_TO_DEV;
+               conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
+               conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
+               chan = host->dma_tx;
+       } else {
+               conf.direction = DMA_DEV_TO_MEM;
+               conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
+               conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
+               chan = host->dma_rx;
+       }
+
+       host->sg_len = dma_map_sg(chan->device->dev,
+                                 data->sg,
+                                 data->sg_len,
+                                 dir);
+
+       if (host->sg_len == 0) {
+               dev_err(mmc_dev(host->mmc),
+                       "Failed to map scatterlist for DMA operation\n");
+               return -EINVAL;
+       }
+
+       dmaengine_slave_config(chan, &conf);
+       desc = dmaengine_prep_slave_sg(chan,
+                                      data->sg,
+                                      host->sg_len,
+                                      conf.direction,
+                                      DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!desc) {
+               dev_err(mmc_dev(host->mmc),
+                       "Failed to allocate DMA %s descriptor",
+                        conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX");
+               goto dma_unmap;
+       }
+
+       dmaengine_submit(desc);
+       dma_async_issue_pending(chan);
+
+       return 0;
+
+dma_unmap:
+       jz4740_mmc_dma_unmap(host, data);
+       return -ENOMEM;
+}
+
+/*----------------------------------------------------------------------------*/
+
 static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
        unsigned int irq, bool enabled)
 {
                        cmdat |= JZ_MMC_CMDAT_WRITE;
                if (cmd->data->flags & MMC_DATA_STREAM)
                        cmdat |= JZ_MMC_CMDAT_STREAM;
+               if (host->use_dma)
+                       cmdat |= JZ_MMC_CMDAT_DMA_EN;
 
                writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
                writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
        struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
        struct mmc_command *cmd = host->req->cmd;
        struct mmc_request *req = host->req;
+       struct mmc_data *data = cmd->data;
        bool timeout = false;
 
        if (cmd->error)
                if (cmd->flags & MMC_RSP_PRESENT)
                        jz4740_mmc_read_response(host, cmd);
 
-               if (!cmd->data)
+               if (!data)
                        break;
 
                jz_mmc_prepare_data_transfer(host);
 
        case JZ4740_MMC_STATE_TRANSFER_DATA:
-               if (cmd->data->flags & MMC_DATA_READ)
-                       timeout = jz4740_mmc_read_data(host, cmd->data);
+               if (host->use_dma) {
+                       /* Use DMA if enabled, data transfer direction was
+                        * defined  before in jz_mmc_prepare_data_transfer().
+                        */
+                       timeout = jz4740_mmc_start_dma_transfer(host, data);
+                       data->bytes_xfered = data->blocks * data->blksz;
+               } else if (data->flags & MMC_DATA_READ)
+                       /* If DMA is not enabled, rely on data flags
+                        * to establish data transfer direction.
+                        */
+                       timeout = jz4740_mmc_read_data(host, data);
                else
-                       timeout = jz4740_mmc_write_data(host, cmd->data);
+                       timeout = jz4740_mmc_write_data(host, data);
 
                if (unlikely(timeout)) {
                        host->state = JZ4740_MMC_STATE_TRANSFER_DATA;
                        break;
                }
 
-               jz4740_mmc_transfer_check_state(host, cmd->data);
+               jz4740_mmc_transfer_check_state(host, data);
 
                timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
                if (unlikely(timeout)) {
        struct mmc_host *mmc;
        struct jz4740_mmc_host *host;
        struct jz4740_mmc_platform_data *pdata;
-       struct resource *res;
 
        pdata = pdev->dev.platform_data;
 
                goto err_free_host;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       host->base = devm_ioremap_resource(&pdev->dev, res);
+       host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       host->base = devm_ioremap_resource(&pdev->dev, host->mem_res);
        if (IS_ERR(host->base)) {
                ret = PTR_ERR(host->base);
+               dev_err(&pdev->dev, "Failed to ioremap base memory\n");
                goto err_free_host;
        }
 
        /* It is not important when it times out, it just needs to timeout. */
        set_timer_slack(&host->timeout_timer, HZ);
 
+       host->use_dma = true;
+       if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
+               host->use_dma = false;
+
        platform_set_drvdata(pdev, host);
        ret = mmc_add_host(mmc);
 
        }
        dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
 
+       dev_info(&pdev->dev, "Using %s, %d-bit mode\n",
+                host->use_dma ? "DMA" : "PIO",
+                (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
+
        return 0;
 
 err_free_irq:
 err_free_gpios:
        jz4740_mmc_free_gpios(pdev);
 err_gpio_bulk_free:
+       if (host->use_dma)
+               jz4740_mmc_release_dma_channels(host);
        jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 err_free_host:
        mmc_free_host(mmc);
        jz4740_mmc_free_gpios(pdev);
        jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
 
+       if (host->use_dma)
+               jz4740_mmc_release_dma_channels(host);
+
        mmc_free_host(host->mmc);
 
        return 0;