From 5a7ccdf845d64b385affdcffaf2defbe9848be15 Mon Sep 17 00:00:00 2001
From: Ram Chandra Jangir <rjangir@codeaurora.org>
Date: Thu, 20 Apr 2017 10:39:00 +0530
Subject: [PATCH] dmaengine: qcom: bam_dma: Add custom data mapping

Add a new function to support for preparing DMA descriptor
for custom data.

Signed-off-by: Abhishek Sahu <absahu@codeaurora.org>
Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
---
 drivers/dma/qcom/bam_dma.c       | 97 +++++++++++++++++++++++++++++++++++++---
 include/linux/dma/qcom_bam_dma.h | 14 ++++++
 include/linux/dmaengine.h        | 14 ++++++
 3 files changed, 119 insertions(+), 6 deletions(-)

--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -49,6 +49,7 @@
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
 #include <linux/pm_runtime.h>
+#include <linux/dma/qcom_bam_dma.h>
 
 #include "../dmaengine.h"
 #include "../virt-dma.h"
@@ -61,11 +62,6 @@ struct bam_desc_hw {
 
 #define BAM_DMA_AUTOSUSPEND_DELAY 100
 
-#define DESC_FLAG_INT BIT(15)
-#define DESC_FLAG_EOT BIT(14)
-#define DESC_FLAG_EOB BIT(13)
-#define DESC_FLAG_NWD BIT(12)
-
 struct bam_async_desc {
 	struct virt_dma_desc vd;
 
@@ -670,6 +666,93 @@ err_out:
 }
 
 /**
+ * bam_prep_dma_custom_mapping - Prep DMA descriptor from custom data
+ *
+ * @chan: dma channel
+ * @data: custom data
+ * @flags: DMA flags
+ */
+static struct dma_async_tx_descriptor *bam_prep_dma_custom_mapping(
+		struct dma_chan *chan,
+		void *data, unsigned long flags)
+{
+	struct bam_chan *bchan = to_bam_chan(chan);
+	struct bam_device *bdev = bchan->bdev;
+	struct bam_async_desc *async_desc;
+	struct qcom_bam_custom_data *desc_data = data;
+	u32 i;
+	struct bam_desc_hw *desc;
+	unsigned int num_alloc = 0;
+
+
+	if (!is_slave_direction(desc_data->dir)) {
+		dev_err(bdev->dev, "invalid dma direction\n");
+		return NULL;
+	}
+
+	/* calculate number of required entries */
+	for (i = 0; i < desc_data->sgl_cnt; i++)
+		num_alloc += DIV_ROUND_UP(
+			sg_dma_len(&desc_data->bam_sgl[i].sgl), BAM_FIFO_SIZE);
+
+	/* allocate enough room to accommodate the number of entries */
+	async_desc = kzalloc(sizeof(*async_desc) +
+			(num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT);
+
+	if (!async_desc)
+		goto err_out;
+
+	if (flags & DMA_PREP_FENCE)
+		async_desc->flags |= DESC_FLAG_NWD;
+
+	if (flags & DMA_PREP_INTERRUPT)
+		async_desc->flags |= DESC_FLAG_EOT;
+	else
+		async_desc->flags |= DESC_FLAG_INT;
+
+	async_desc->num_desc = num_alloc;
+	async_desc->curr_desc = async_desc->desc;
+	async_desc->dir = desc_data->dir;
+
+	/* fill in temporary descriptors */
+	desc = async_desc->desc;
+	for (i = 0; i < desc_data->sgl_cnt; i++) {
+		unsigned int remainder;
+		unsigned int curr_offset = 0;
+
+		remainder = sg_dma_len(&desc_data->bam_sgl[i].sgl);
+
+		do {
+			desc->addr = cpu_to_le32(
+				sg_dma_address(&desc_data->bam_sgl[i].sgl) +
+						 curr_offset);
+
+			if (desc_data->bam_sgl[i].dma_flags)
+				desc->flags |= cpu_to_le16(
+					desc_data->bam_sgl[i].dma_flags);
+
+			if (remainder > BAM_FIFO_SIZE) {
+				desc->size = cpu_to_le16(BAM_FIFO_SIZE);
+				remainder -= BAM_FIFO_SIZE;
+				curr_offset += BAM_FIFO_SIZE;
+			} else {
+				desc->size = cpu_to_le16(remainder);
+				remainder = 0;
+			}
+
+			async_desc->length += desc->size;
+			desc++;
+		} while (remainder > 0);
+	}
+
+	return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
+
+err_out:
+	kfree(async_desc);
+	return NULL;
+}
+
+/**
  * bam_dma_terminate_all - terminate all transactions on a channel
  * @bchan: bam dma channel
  *
@@ -960,7 +1043,7 @@ static void bam_start_dma(struct bam_cha
 
 	/* set any special flags on the last descriptor */
 	if (async_desc->num_desc == async_desc->xfer_len)
-		desc[async_desc->xfer_len - 1].flags =
+		desc[async_desc->xfer_len - 1].flags |=
 					cpu_to_le16(async_desc->flags);
 	else
 		desc[async_desc->xfer_len - 1].flags |=
@@ -1237,6 +1320,8 @@ static int bam_dma_probe(struct platform
 	bdev->common.device_alloc_chan_resources = bam_alloc_chan;
 	bdev->common.device_free_chan_resources = bam_free_chan;
 	bdev->common.device_prep_slave_sg = bam_prep_slave_sg;
+	bdev->common.device_prep_dma_custom_mapping =
+		bam_prep_dma_custom_mapping;
 	bdev->common.device_config = bam_slave_config;
 	bdev->common.device_pause = bam_pause;
 	bdev->common.device_resume = bam_resume;
--- a/include/linux/dma/qcom_bam_dma.h
+++ b/include/linux/dma/qcom_bam_dma.h
@@ -65,6 +65,19 @@ enum bam_command_type {
 };
 
 /*
+ * QCOM BAM DMA custom data
+ *
+ * @sgl_cnt: number of sgl in bam_sgl
+ * @dir: DMA data transfer direction
+ * @bam_sgl: BAM SGL pointer
+ */
+struct qcom_bam_custom_data {
+	u32 sgl_cnt;
+	enum dma_transfer_direction dir;
+	struct qcom_bam_sgl *bam_sgl;
+};
+
+/*
  * qcom_bam_sg_init_table - Init QCOM BAM SGL
  * @bam_sgl: bam sgl
  * @nents: number of entries in bam sgl
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -692,6 +692,8 @@ struct dma_filter {
  *	be called after period_len bytes have been transferred.
  * @device_prep_interleaved_dma: Transfer expression in a generic way.
  * @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address
+ * @device_prep_dma_custom_mapping: prepares a dma operation from dma driver
+ * 	specific custom data
  * @device_config: Pushes a new configuration to a channel, return 0 or an error
  *	code
  * @device_pause: Pauses any transfer happening on a channel. Returns
@@ -783,6 +785,9 @@ struct dma_device {
 	struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)(
 		struct dma_chan *chan, dma_addr_t dst, u64 data,
 		unsigned long flags);
+	struct dma_async_tx_descriptor *(*device_prep_dma_custom_mapping)(
+		struct dma_chan *chan, void *data,
+		unsigned long flags);
 
 	int (*device_config)(struct dma_chan *chan,
 			     struct dma_slave_config *config);
@@ -899,6 +904,15 @@ static inline struct dma_async_tx_descri
 			src_sg, src_nents, flags);
 }
 
+static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_custom_mapping(
+		struct dma_chan *chan,
+		void *data,
+		unsigned long flags)
+{
+	return chan->device->device_prep_dma_custom_mapping(chan, data,
+			flags);
+}
+
 /**
  * dmaengine_terminate_all() - Terminate all active DMA transfers
  * @chan: The channel for which to terminate the transfers