Re: [PATCH] dmaengine: axi-dmac: Split too large segments

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2019-03-08 at 14:58 +0200, Alexandru Ardelean wrote:
> From: Lars-Peter Clausen <lars@xxxxxxxxxx>
> 
> The axi-dmac driver currently rejects transfers with segments that are
> larger than what the hardware can handle.
> 
> Re-work the driver so that these large segments are split into multiple
> segments instead where each segment is smaller or equal to the maximum
> segment size.
> 
> This allows the driver to handle transfers with segments of arbitrary
> size.

Error on this one.
I forgot to add the Changelogs.

> 
> Signed-off-by: Lars-Peter Clausen <lars@xxxxxxxxxx>
> Signed-off-by: Bogdan Togorean <bogdan.togorean@xxxxxxxxxx>
> Signed-off-by: Alexandru Ardelean <alex.ardelean@xxxxxxxxxx>
> ---
>  drivers/dma/dma-axi-dmac.c | 81 ++++++++++++++++++++++++++++----------
>  1 file changed, 61 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
> index ffc0adc2f6ce..0fe3a931d8d5 100644
> --- a/drivers/dma/dma-axi-dmac.c
> +++ b/drivers/dma/dma-axi-dmac.c
> @@ -166,7 +166,7 @@ static int axi_dmac_dest_is_mem(struct axi_dmac_chan
> *chan)
>  
>  static bool axi_dmac_check_len(struct axi_dmac_chan *chan, unsigned int
> len)
>  {
> -	if (len == 0 || len > chan->max_length)
> +	if (len == 0)
>  		return false;
>  	if ((len & chan->align_mask) != 0) /* Not aligned */
>  		return false;
> @@ -379,6 +379,49 @@ static struct axi_dmac_desc
> *axi_dmac_alloc_desc(unsigned int num_sgs)
>  	return desc;
>  }
>  
> +static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan
> *chan,
> +	enum dma_transfer_direction direction, dma_addr_t addr,
> +	unsigned int num_periods, unsigned int period_len,
> +	struct axi_dmac_sg *sg)
> +{
> +	unsigned int num_segments, i;
> +	unsigned int segment_size;
> +	unsigned int len;
> +
> +	/* Split into multiple equally sized segments if necessary */
> +	num_segments = DIV_ROUND_UP(period_len, chan->max_length);
> +	segment_size = DIV_ROUND_UP(period_len, num_segments);
> +	/* Take care of alignment */
> +	segment_size = ((segment_size - 1) | chan->align_mask) + 1;
> +
> +	for (i = 0; i < num_periods; i++) {
> +		len = period_len;
> +
> +		while (len > segment_size) {
> +			if (direction == DMA_DEV_TO_MEM)
> +				sg->dest_addr = addr;
> +			else
> +				sg->src_addr = addr;
> +			sg->x_len = segment_size;
> +			sg->y_len = 1;
> +			sg++;
> +			addr += segment_size;
> +			len -= segment_size;
> +		}
> +
> +		if (direction == DMA_DEV_TO_MEM)
> +			sg->dest_addr = addr;
> +		else
> +			sg->src_addr = addr;
> +		sg->x_len = len;
> +		sg->y_len = 1;
> +		sg++;
> +		addr += len;
> +	}
> +
> +	return sg;
> +}
> +
>  static struct dma_async_tx_descriptor *axi_dmac_prep_slave_sg(
>  	struct dma_chan *c, struct scatterlist *sgl,
>  	unsigned int sg_len, enum dma_transfer_direction direction,
> @@ -386,16 +429,24 @@ static struct dma_async_tx_descriptor
> *axi_dmac_prep_slave_sg(
>  {
>  	struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
>  	struct axi_dmac_desc *desc;
> +	struct axi_dmac_sg *dsg;
>  	struct scatterlist *sg;
> +	unsigned int num_sgs;
>  	unsigned int i;
>  
>  	if (direction != chan->direction)
>  		return NULL;
>  
> -	desc = axi_dmac_alloc_desc(sg_len);
> +	num_sgs = 0;
> +	for_each_sg(sgl, sg, sg_len, i)
> +		num_sgs += DIV_ROUND_UP(sg_dma_len(sg), chan->max_length);
> +
> +	desc = axi_dmac_alloc_desc(num_sgs);
>  	if (!desc)
>  		return NULL;
>  
> +	dsg = desc->sg;
> +
>  	for_each_sg(sgl, sg, sg_len, i) {
>  		if (!axi_dmac_check_addr(chan, sg_dma_address(sg)) ||
>  		    !axi_dmac_check_len(chan, sg_dma_len(sg))) {
> @@ -403,12 +454,8 @@ static struct dma_async_tx_descriptor
> *axi_dmac_prep_slave_sg(
>  			return NULL;
>  		}
>  
> -		if (direction == DMA_DEV_TO_MEM)
> -			desc->sg[i].dest_addr = sg_dma_address(sg);
> -		else
> -			desc->sg[i].src_addr = sg_dma_address(sg);
> -		desc->sg[i].x_len = sg_dma_len(sg);
> -		desc->sg[i].y_len = 1;
> +		dsg = axi_dmac_fill_linear_sg(chan, direction,
> sg_dma_address(sg), 1,
> +			sg_dma_len(sg), dsg);
>  	}
>  
>  	desc->cyclic = false;
> @@ -423,7 +470,7 @@ static struct dma_async_tx_descriptor
> *axi_dmac_prep_dma_cyclic(
>  {
>  	struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
>  	struct axi_dmac_desc *desc;
> -	unsigned int num_periods, i;
> +	unsigned int num_periods, num_segments;
>  
>  	if (direction != chan->direction)
>  		return NULL;
> @@ -436,20 +483,14 @@ static struct dma_async_tx_descriptor
> *axi_dmac_prep_dma_cyclic(
>  		return NULL;
>  
>  	num_periods = buf_len / period_len;
> +	num_segments = DIV_ROUND_UP(period_len, chan->max_length);
>  
> -	desc = axi_dmac_alloc_desc(num_periods);
> +	desc = axi_dmac_alloc_desc(num_periods * num_segments);
>  	if (!desc)
>  		return NULL;
>  
> -	for (i = 0; i < num_periods; i++) {
> -		if (direction == DMA_DEV_TO_MEM)
> -			desc->sg[i].dest_addr = buf_addr;
> -		else
> -			desc->sg[i].src_addr = buf_addr;
> -		desc->sg[i].x_len = period_len;
> -		desc->sg[i].y_len = 1;
> -		buf_addr += period_len;
> -	}
> +	axi_dmac_fill_linear_sg(chan, direction, buf_addr, num_periods,
> +		period_len, desc->sg);
>  
>  	desc->cyclic = true;
>  
> @@ -647,7 +688,7 @@ static int axi_dmac_probe(struct platform_device
> *pdev)
>  	of_node_put(of_channels);
>  
>  	pdev->dev.dma_parms = &dmac->dma_parms;
> -	dma_set_max_seg_size(&pdev->dev, dmac->chan.max_length);
> +	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
>  
>  	dma_dev = &dmac->dma_dev;
>  	dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);




[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux