samsung: dma: add samsung dma
authorEunok Jo <eunok25.jo@samsung.com>
Mon, 22 May 2017 10:48:28 +0000 (19:48 +0900)
committerTaekki Kim <taekki.kim@samsung.com>
Mon, 14 May 2018 05:42:48 +0000 (14:42 +0900)
Change-Id: I817574379d67e5605517dd409cf8f012f59b3653
Signed-off-by: Eunok Jo <eunok25.jo@samsung.com>
drivers/dma/Kconfig
drivers/dma/Makefile
drivers/dma/samsung-dma.c [new file with mode: 0644]

index fadc4d8783bd8a48817867eeb0640e49b193d922..db31643a23c642675022bb29e9df44746379d354 100644 (file)
@@ -424,6 +424,12 @@ config PCH_DMA
          ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
          ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
 
+config SAMSUNG_DMADEV
+       bool
+       select PL330_DMA if (ARCH_EXYNOS9)
+       help
+         Use DMA device engine for PL330 DMA controller.
+
 config PL330_DMA
        tristate "DMA API Driver for PL330"
        select DMA_ENGINE
index 9d0156b502946a63fe93e344dc50fe0680ff7608..bb21c35b8213e3d89785ea811b09185c29fed7e9 100644 (file)
@@ -53,7 +53,7 @@ obj-$(CONFIG_MXS_DMA) += mxs-dma.o
 obj-$(CONFIG_MX3_IPU) += ipu/
 obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
-obj-$(CONFIG_PL330_DMA) += pl330.o
+obj-$(CONFIG_PL330_DMA) += pl330.o samsung-dma.o
 obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
 obj-$(CONFIG_PXA_DMA) += pxa_dma.o
 obj-$(CONFIG_RENESAS_DMA) += sh/
diff --git a/drivers/dma/samsung-dma.c b/drivers/dma/samsung-dma.c
new file mode 100644 (file)
index 0000000..3814dc9
--- /dev/null
@@ -0,0 +1,162 @@
+/* linux/arch/arm/plat-samsung/dma-ops.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Samsung DMA Operations
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/amba/pl330.h>
+#include <linux/scatterlist.h>
+#include <linux/export.h>
+#include <linux/dma/dma-pl330.h>
+
+static unsigned long samsung_dmadev_request(enum dma_ch dma_ch,
+                               struct samsung_dma_req *param,
+                               struct device *dev, char *ch_name)
+{
+       dma_cap_mask_t mask;
+       unsigned long channel;
+
+       dma_cap_zero(mask);
+       dma_cap_set(param->cap, mask);
+
+       if (dev->of_node)
+       {
+               channel = (unsigned long)dma_request_slave_channel(dev, ch_name);
+               return channel;
+       }
+       else
+               return (u64)dma_request_channel(mask, pl330_filter,
+                                                       (void *)dma_ch);
+}
+
+static int samsung_dmadev_release(unsigned long ch, void *param)
+{
+       dma_release_channel((struct dma_chan *)ch);
+
+       return 0;
+}
+
+static int samsung_dmadev_config(unsigned long ch,
+                               struct samsung_dma_config *param)
+{
+       struct dma_chan *chan = (struct dma_chan *)ch;
+       struct dma_slave_config slave_config;
+
+       if (param->direction == DMA_DEV_TO_MEM) {
+               memset(&slave_config, 0, sizeof(struct dma_slave_config));
+               slave_config.direction = param->direction;
+               slave_config.src_addr = param->fifo;
+               slave_config.src_addr_width = param->width;
+               if (param->maxburst)
+                       slave_config.src_maxburst = param->maxburst;
+               else
+                       slave_config.src_maxburst = 1;
+               dmaengine_slave_config(chan, &slave_config);
+       } else if (param->direction == DMA_MEM_TO_DEV) {
+               memset(&slave_config, 0, sizeof(struct dma_slave_config));
+               slave_config.direction = param->direction;
+               slave_config.dst_addr = param->fifo;
+               slave_config.dst_addr_width = param->width;
+               if (param->maxburst)
+                       slave_config.dst_maxburst = param->maxburst;
+               else
+                       slave_config.dst_maxburst = 1;
+               dmaengine_slave_config(chan, &slave_config);
+       } else {
+               pr_warn("unsupported direction\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int samsung_dmadev_prepare(unsigned long ch,
+                       struct samsung_dma_prep *param)
+{
+       struct scatterlist sg;
+       struct dma_chan *chan = (struct dma_chan *)ch;
+       struct dma_async_tx_descriptor *desc;
+
+       switch (param->cap) {
+       case DMA_SLAVE:
+               sg_init_table(&sg, 1);
+               sg_dma_len(&sg) = (unsigned int)param->len;
+               sg_set_page(&sg, pfn_to_page(PFN_DOWN(param->buf)),
+                           param->len, offset_in_page(param->buf));
+               sg_dma_address(&sg) = param->buf;
+
+               desc = dmaengine_prep_slave_sg(chan,
+                       &sg, 1, param->direction, DMA_PREP_INTERRUPT);
+               break;
+       case DMA_CYCLIC:
+               desc = chan->device->device_prep_dma_cyclic(chan,
+                               param->buf, param->len, param->period,
+                               param->direction, true, &param->infiniteloop);
+               break;
+       default:
+               dev_err(&chan->dev->device, "unsupported format\n");
+               return -EFAULT;
+       }
+
+       if (!desc) {
+               dev_err(&chan->dev->device, "cannot prepare cyclic dma\n");
+               return -EFAULT;
+       }
+
+       desc->callback = param->fp;
+       desc->callback_param = param->fp_param;
+
+       dmaengine_submit((struct dma_async_tx_descriptor *)desc);
+
+       return 0;
+}
+
+static inline int samsung_dmadev_trigger(unsigned long ch)
+{
+       dma_async_issue_pending((struct dma_chan *)ch);
+
+       return 0;
+}
+
+static inline int samsung_dmadev_getposition(unsigned long ch,
+               dma_addr_t *src, dma_addr_t *dst)
+{
+       return pl330_dma_getposition((struct dma_chan *)ch, src, dst);
+}
+
+static inline int samsung_dmadev_flush(unsigned long ch)
+{
+       return dmaengine_terminate_all((struct dma_chan *)ch);
+}
+
+static inline int samsung_dmadev_debug(unsigned long ch)
+{
+       return pl330_dma_debug((struct dma_chan *)ch);
+}
+
+static struct samsung_dma_ops dmadev_ops = {
+       .request        = samsung_dmadev_request,
+       .release        = samsung_dmadev_release,
+       .config         = samsung_dmadev_config,
+       .prepare        = samsung_dmadev_prepare,
+       .trigger        = samsung_dmadev_trigger,
+       .started        = NULL,
+       .getposition    = samsung_dmadev_getposition,
+       .flush          = samsung_dmadev_flush,
+       .stop           = samsung_dmadev_flush,
+       .debug          = samsung_dmadev_debug,
+};
+
+void *samsung_dmadev_get_ops(void)
+{
+       return &dmadev_ops;
+}
+EXPORT_SYMBOL(samsung_dmadev_get_ops);