diff options
Diffstat (limited to 'drivers/dma/xilinx/xilinx_dma.c')
| -rw-r--r-- | drivers/dma/xilinx/xilinx_dma.c | 94 | 
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index a34d8f0ceed8..fabff602065f 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -2173,6 +2173,99 @@ error:  }  /** + * 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   * @sgl: scatterlist to transfer to/from @@ -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;  | 
