Commit 38433a6f authored by Folker Schwesinger's avatar Folker Schwesinger Committed by Vinod Koul
Browse files

dmaengine: xilinx_dma: Support descriptor setup from dma_vecs



The DMAEngine provides an interface for obtaining DMA transaction
descriptors from an array of scatter gather buffers represented by
struct dma_vec. This interface is used in the DMABUF API of the IIO
framework [1][2].
To enable DMABUF support through the IIO framework for the Xilinx DMA,
implement callback .device_prep_peripheral_dma_vec() of struct
dma_device in the driver.

[1]: 7a86d469 ("iio: buffer-dmaengine: Support new DMABUF based userspace API")
[2]: 5878853f ("dmaengine: Add API function dmaengine_prep_peripheral_dma_vec()")

Signed-off-by: default avatarFolker Schwesinger <dev@folker-schwesinger.de>
Reviewed-by: default avatarSuraj Gupta <suraj.gupta2@amd.com>
Reviewed-by: default avatarRadhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Link: https://lore.kernel.org/r/DCCKQLKOZC06.2H6LJ8RJQJNV2@folker-schwesinger.de


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent d9a3e992
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -2172,6 +2172,99 @@ xilinx_cdma_prep_memcpy(struct dma_chan *dchan, dma_addr_t dma_dst,
	return NULL;
}

/**
 * xilinx_dma_prep_peripheral_dma_vec - prepare descriptors for a DMA_SLAVE
 *	transaction from DMA vectors
 * @dchan: DMA channel
 * @vecs: Array of DMA vectors that should be transferred
 * @nb: number of entries in @vecs
 * @direction: DMA direction
 * @flags: transfer ack flags
 *
 * Return: Async transaction descriptor on success and NULL on failure
 */
static struct dma_async_tx_descriptor *xilinx_dma_prep_peripheral_dma_vec(
	struct dma_chan *dchan, const struct dma_vec *vecs, size_t nb,
	enum dma_transfer_direction direction, unsigned long flags)
{
	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
	struct xilinx_dma_tx_descriptor *desc;
	struct xilinx_axidma_tx_segment *segment, *head, *prev = NULL;
	size_t copy;
	size_t sg_used;
	unsigned int i;

	if (!is_slave_direction(direction) || direction != chan->direction)
		return NULL;

	desc = xilinx_dma_alloc_tx_descriptor(chan);
	if (!desc)
		return NULL;

	dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
	desc->async_tx.tx_submit = xilinx_dma_tx_submit;

	/* Build transactions using information from DMA vectors */
	for (i = 0; i < nb; i++) {
		sg_used = 0;

		/* Loop until the entire dma_vec entry is used */
		while (sg_used < vecs[i].len) {
			struct xilinx_axidma_desc_hw *hw;

			/* Get a free segment */
			segment = xilinx_axidma_alloc_tx_segment(chan);
			if (!segment)
				goto error;

			/*
			 * Calculate the maximum number of bytes to transfer,
			 * making sure it is less than the hw limit
			 */
			copy = xilinx_dma_calc_copysize(chan, vecs[i].len,
					sg_used);
			hw = &segment->hw;

			/* Fill in the descriptor */
			xilinx_axidma_buf(chan, hw, vecs[i].addr, sg_used, 0);
			hw->control = copy;

			if (prev)
				prev->hw.next_desc = segment->phys;

			prev = segment;
			sg_used += copy;

			/*
			 * Insert the segment into the descriptor segments
			 * list.
			 */
			list_add_tail(&segment->node, &desc->segments);
		}
	}

	head = list_first_entry(&desc->segments, struct xilinx_axidma_tx_segment, node);
	desc->async_tx.phys = head->phys;

	/* For the last DMA_MEM_TO_DEV transfer, set EOP */
	if (chan->direction == DMA_MEM_TO_DEV) {
		segment->hw.control |= XILINX_DMA_BD_SOP;
		segment = list_last_entry(&desc->segments,
					  struct xilinx_axidma_tx_segment,
					  node);
		segment->hw.control |= XILINX_DMA_BD_EOP;
	}

	if (chan->xdev->has_axistream_connected)
		desc->async_tx.metadata_ops = &xilinx_dma_metadata_ops;

	return &desc->async_tx;

error:
	xilinx_dma_free_tx_descriptor(chan, desc);
	return NULL;
}

/**
 * xilinx_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
 * @dchan: DMA channel
@@ -3180,6 +3273,7 @@ static int xilinx_dma_probe(struct platform_device *pdev)
	xdev->common.device_config = xilinx_dma_device_config;
	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
		dma_cap_set(DMA_CYCLIC, xdev->common.cap_mask);
		xdev->common.device_prep_peripheral_dma_vec = xilinx_dma_prep_peripheral_dma_vec;
		xdev->common.device_prep_slave_sg = xilinx_dma_prep_slave_sg;
		xdev->common.device_prep_dma_cyclic =
					  xilinx_dma_prep_dma_cyclic;