Re: [PATCH 5/5] dmaengine: xdmac: Add interleaved transfer support

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

 



On Mon, May 04, 2015 at 04:32:09PM +0530, Vinod Koul wrote:
> On Tue, Apr 28, 2015 at 11:03:23AM +0200, Maxime Ripard wrote:
> > The XDMAC supports interleaved tranfers through its flexible descriptor
> > configuration.
> > 
> > Add support for that kind of transfers to the dmaengine driver.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx>
> > ---
> >  drivers/dma/at_xdmac.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 219 insertions(+)
> > 
> > diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
> > index 67e566b2b24f..3c40c5ef043d 100644
> > --- a/drivers/dma/at_xdmac.c
> > +++ b/drivers/dma/at_xdmac.c
> > @@ -485,6 +485,19 @@ static void at_xdmac_queue_desc(struct dma_chan *chan,
> >  		__func__, prev, prev->lld.mbr_nda);
> >  }
> >  
> > +static inline void at_xdmac_increment_block_count(struct dma_chan *chan,
> > +						  struct at_xdmac_desc *desc)
> > +{
> > +	if (!desc)
> > +		return;
> > +
> > +	desc->lld.mbr_bc++;
> > +
> > +	dev_dbg(chan2dev(chan),
> > +		"%s: incrementing the block count of the desc 0x%p\n",
> > +		__func__, desc);
> > +}
> > +
> >  static struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec,
> >  				       struct of_dma *of_dma)
> >  {
> > @@ -782,6 +795,210 @@ static inline u32 at_xdmac_align_width(struct dma_chan *chan, dma_addr_t addr)
> >  	return width;
> >  }
> >  
> > +static struct at_xdmac_desc *
> > +at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
> > +				struct at_xdmac_chan *atchan,
> > +				struct at_xdmac_desc *prev,
> > +				dma_addr_t src, dma_addr_t dst,
> > +				struct dma_interleaved_template *xt,
> > +				struct data_chunk *chunk)
> > +{
> > +	struct at_xdmac_desc	*desc = NULL;
> this seems superflous
> 
> > +	u32			dwidth;
> > +	unsigned long		flags;
> > +	size_t			ublen;
> > +	/*
> > +	 * WARNING: The channel configuration is set here since there is no
> > +	 * dmaengine_slave_config call in this case. Moreover we don't know the
> > +	 * direction, it involves we can't dynamically set the source and dest
> > +	 * interface so we have to use the same one. Only interface 0 allows EBI
> > +	 * access. Hopefully we can access DDR through both ports (at least on
> > +	 * SAMA5D4x), so we can use the same interface for source and dest,
> > +	 * that solves the fact we don't know the direction.
> > +	 */
> > +	u32			chan_cc = AT_XDMAC_CC_DIF(0)
> > +					| AT_XDMAC_CC_SIF(0)
> > +					| AT_XDMAC_CC_MBSIZE_SIXTEEN
> > +					| AT_XDMAC_CC_TYPE_MEM_TRAN;
> > +
> > +	dwidth = at_xdmac_align_width(chan, src | dst | chunk->size);
> > +	if (chunk->size >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) {
> > +		dev_dbg(chan2dev(chan),
> > +			"%s: chunk too big (%d, max size %lu)...\n",
> > +			__func__, chunk->size,
> > +			AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth);
> > +		return NULL;
> > +	}
> > +
> > +	if (prev)
> > +		dev_dbg(chan2dev(chan),
> > +			"Adding items at the end of desc 0x%p\n", prev);
> > +
> > +	if (xt->src_inc) {
> > +		if (xt->src_sgl)
> > +			chan_cc |=  AT_XDMAC_CC_SAM_UBS_DS_AM;
> > +		else
> > +			chan_cc |=  AT_XDMAC_CC_SAM_INCREMENTED_AM;
> > +	}
> > +
> > +	if (xt->dst_inc) {
> > +		if (xt->dst_sgl)
> > +			chan_cc |=  AT_XDMAC_CC_DAM_UBS_DS_AM;
> > +		else
> > +			chan_cc |=  AT_XDMAC_CC_DAM_INCREMENTED_AM;
> > +	}
> > +
> > +	spin_lock_irqsave(&atchan->lock, flags);
> > +	desc = at_xdmac_get_desc(atchan);
> > +	spin_unlock_irqrestore(&atchan->lock, flags);
> > +	if (!desc) {
> > +		dev_err(chan2dev(chan), "can't get descriptor\n");
> > +		return NULL;
> > +	}
> > +
> > +	chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth);
> > +
> > +	ublen = chunk->size >> dwidth;
> > +
> > +	desc->lld.mbr_sa = src;
> > +	desc->lld.mbr_da = dst;
> > +
> > +	if (xt->src_inc && xt->src_sgl) {
> > +		if (chunk->src_icg)
> > +			desc->lld.mbr_sus = chunk->src_icg;
> > +		else
> > +			desc->lld.mbr_sus = chunk->icg;
> > +	}
> > +
> > +	if (xt->dst_inc && xt->dst_sgl) {
> > +		if (chunk->dst_icg)
> > +			desc->lld.mbr_dus = chunk->dst_icg;
> > +		else
> > +			desc->lld.mbr_dus = chunk->icg;
> > +	}
> > +
> > +	desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3
> > +		| AT_XDMAC_MBR_UBC_NDEN
> > +		| AT_XDMAC_MBR_UBC_NSEN
> > +		| ublen;
> > +	desc->lld.mbr_cfg = chan_cc;
> > +
> > +	dev_dbg(chan2dev(chan),
> > +		"%s: lld: mbr_sa=0x%08x, mbr_da=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
> > +		__func__, desc->lld.mbr_sa, desc->lld.mbr_da,
> > +		desc->lld.mbr_ubc, desc->lld.mbr_cfg);
> > +
> > +	/* Chain lld. */
> > +	if (prev)
> > +		at_xdmac_queue_desc(chan, prev, desc);
> otherwise you don't queue... what did i miss?

It is not about queueing but chaining, if there is only one descriptor,
this step is not needed.

> 
> > +
> > +	return desc;
> > +}
> > +
> > +static struct dma_async_tx_descriptor *
> > +at_xdmac_prep_interleaved(struct dma_chan *chan,
> > +			  struct dma_interleaved_template *xt,
> > +			  unsigned long flags)
> > +{
> > +	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
> > +	struct at_xdmac_desc	*prev = NULL, *first = NULL;
> > +	struct data_chunk	*chunk, *prev_chunk = NULL;
> > +	dma_addr_t		dst_addr, src_addr;
> > +	size_t			prev_src_skip = 0, src_skip = 0, len = 0;
> > +	size_t			prev_dst_skip = 0, dst_skip = 0;
> dont see why src_skip and dst_skip should be set?
>

Same for prev_ ones which seem useless as Vinod said.

> > +	size_t			prev_dst_icg = 0, prev_src_icg = 0;
> > +	int			i;
> > +
> > +	if (!xt || (xt->numf != 1) || (xt->dir != DMA_MEM_TO_MEM))
> > +		return NULL;
> > +
> > +	dev_dbg(chan2dev(chan), "%s: src=0x%08x, dest=0x%08x, numf=%d, frame_size=%d, flags=0x%lx\n",
> > +		__func__, xt->src_start, xt->dst_start,	xt->numf,
> > +		xt->frame_size, flags);
> > +
> > +	src_addr = xt->src_start;
> > +	dst_addr = xt->dst_start;
> > +
> > +	for (i = 0; i < xt->frame_size; i++) {
> > +		struct at_xdmac_desc *desc;
> > +		size_t src_icg = 0, dst_icg = 0;
> > +
> > +		chunk = xt->sgl + i;
> > +
> > +		if (xt->dst_inc) {
> > +			if (chunk->dst_icg)
> > +				dst_icg = chunk->dst_icg;
> > +			else if (xt->dst_sgl)
> > +				dst_icg = chunk->icg;
> > +		}
> > +
> > +		if (xt->src_inc) {
> > +			if (chunk->src_icg)
> > +				src_icg = chunk->src_icg;
> > +			else if (xt->src_sgl)
> > +				src_icg = chunk->icg;
> > +		}
> perhpas a common warpper for this logic?
> 
> > +
> > +		src_skip = chunk->size + src_icg;
> > +		dst_skip = chunk->size + dst_icg;
> > +
> > +		dev_dbg(chan2dev(chan),
> > +			"%s: chunk size=%d, src icg=%d, dst icg=%d\n",
> > +			__func__, chunk->size, src_icg, dst_icg);
> > +
> > +		/*
> > +		 * Handle the case where we just have the same
> > +		 * transfer to setup, we can just increase the
> > +		 * block number and reuse the same descriptor.
> > +		 */
> > +		if (prev_chunk && prev &&
> > +		    (prev_chunk->size == chunk->size) &&
> > +		    (prev_src_icg == src_icg) &&
> > +		    (prev_dst_icg == dst_icg)) {
> the variables prev_src_icg and prev_dst_icg are initailzed to zero but never
> updated, seems to have missing stuff
> 
> > +			dev_dbg(chan2dev(chan),
> > +				"%s: same configuration that the previous chunk, merging the descriptors...\n",
> > +				__func__);
> > +			at_xdmac_increment_block_count(chan, prev);
> > +			continue;
> > +		}
> > +
> > +		desc = at_xdmac_interleaved_queue_desc(chan, atchan,
> > +						       prev,
> > +						       src_addr, dst_addr,
> > +						       xt, chunk);
> > +		if (!desc) {
> > +			list_splice_init(&first->descs_list,
> > +					 &atchan->free_descs_list);
> > +			return NULL;
> > +		}
> > +
> > +		if (!first)
> > +			first = desc;
> > +
> > +		dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
> > +			__func__, desc, first);
> > +		list_add_tail(&desc->desc_node, &first->descs_list);
> > +
> > +		if (xt->src_sgl)
> > +			src_addr += src_skip;
> > +
> > +		if (xt->dst_sgl)
> > +			dst_addr += dst_skip;
> > +
> > +		len += chunk->size;
> > +		prev_chunk = chunk;
> > +		prev_dst_skip = dst_skip;
> > +		prev_src_skip = src_skip;
> these two values are not used anywhere so why keep track of it?
> 
> > +		prev = desc;
> > +	}
> > +
> > +	first->tx_dma_desc.cookie = -EBUSY;
> > +	first->tx_dma_desc.flags = flags;
> > +	first->xfer_size = len;
> > +
> > +	return &first->tx_dma_desc;
> > +}
> 
> Thanks
> -- 
> ~Vinod
> 
> > +
> >  static struct dma_async_tx_descriptor *
> >  at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
> >  			 size_t len, unsigned long flags)
> > @@ -1404,6 +1621,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
> >  	}
> >  
> >  	dma_cap_set(DMA_CYCLIC, atxdmac->dma.cap_mask);
> > +	dma_cap_set(DMA_INTERLEAVE, atxdmac->dma.cap_mask);
> >  	dma_cap_set(DMA_MEMCPY, atxdmac->dma.cap_mask);
> >  	dma_cap_set(DMA_SLAVE, atxdmac->dma.cap_mask);
> >  	/*
> > @@ -1417,6 +1635,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
> >  	atxdmac->dma.device_tx_status			= at_xdmac_tx_status;
> >  	atxdmac->dma.device_issue_pending		= at_xdmac_issue_pending;
> >  	atxdmac->dma.device_prep_dma_cyclic		= at_xdmac_prep_dma_cyclic;
> > +	atxdmac->dma.device_prep_interleaved_dma	= at_xdmac_prep_interleaved;
> >  	atxdmac->dma.device_prep_dma_memcpy		= at_xdmac_prep_dma_memcpy;
> >  	atxdmac->dma.device_prep_slave_sg		= at_xdmac_prep_slave_sg;
> >  	atxdmac->dma.device_config			= at_xdmac_device_config;
> > -- 
> > 2.3.6
> > 
> 
> -- 
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[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