Skip to content

Commit

Permalink
dmaengine: xdmac: Add scatter gathered memset support
Browse files Browse the repository at this point in the history
The XDMAC also supports memset operations over discontiguous areas. Add the
necessary logic to support this.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
  • Loading branch information
Maxime Ripard authored and Vinod Koul committed Aug 6, 2015
1 parent 50c7cd2 commit 67a6eed
Showing 1 changed file with 165 additions and 1 deletion.
166 changes: 165 additions & 1 deletion drivers/dma/at_xdmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,7 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
* 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_DAM_INCREMENTED_AM
u32 chan_cc = AT_XDMAC_CC_DAM_UBS_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
Expand Down Expand Up @@ -1201,6 +1201,168 @@ at_xdmac_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
return &desc->tx_dma_desc;
}

static struct dma_async_tx_descriptor *
at_xdmac_prep_dma_memset_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, int value,
unsigned long flags)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
struct at_xdmac_desc *desc, *pdesc = NULL,
*ppdesc = NULL, *first = NULL;
struct scatterlist *sg, *psg = NULL, *ppsg = NULL;
size_t stride = 0, pstride = 0, len = 0;
int i;

if (!sgl)
return NULL;

dev_dbg(chan2dev(chan), "%s: sg_len=%d, value=0x%x, flags=0x%lx\n",
__func__, sg_len, value, flags);

/* Prepare descriptors. */
for_each_sg(sgl, sg, sg_len, i) {
dev_dbg(chan2dev(chan), "%s: dest=0x%08x, len=%d, pattern=0x%x, flags=0x%lx\n",
__func__, sg_dma_address(sg), sg_dma_len(sg),
value, flags);
desc = at_xdmac_memset_create_desc(chan, atchan,
sg_dma_address(sg),
sg_dma_len(sg),
value);
if (!desc && first)
list_splice_init(&first->descs_list,
&atchan->free_descs_list);

if (!first)
first = desc;

/* Update our strides */
pstride = stride;
if (psg)
stride = sg_dma_address(sg) -
(sg_dma_address(psg) + sg_dma_len(psg));

/*
* The scatterlist API gives us only the address and
* length of each elements.
*
* Unfortunately, we don't have the stride, which we
* will need to compute.
*
* That make us end up in a situation like this one:
* len stride len stride len
* +-------+ +-------+ +-------+
* | N-2 | | N-1 | | N |
* +-------+ +-------+ +-------+
*
* We need all these three elements (N-2, N-1 and N)
* to actually take the decision on whether we need to
* queue N-1 or reuse N-2.
*
* We will only consider N if it is the last element.
*/
if (ppdesc && pdesc) {
if ((stride == pstride) &&
(sg_dma_len(ppsg) == sg_dma_len(psg))) {
dev_dbg(chan2dev(chan),
"%s: desc 0x%p can be merged with desc 0x%p\n",
__func__, pdesc, ppdesc);

/*
* Increment the block count of the
* N-2 descriptor
*/
at_xdmac_increment_block_count(chan, ppdesc);
ppdesc->lld.mbr_dus = stride;

/*
* Put back the N-1 descriptor in the
* free descriptor list
*/
list_add_tail(&pdesc->desc_node,
&atchan->free_descs_list);

/*
* Make our N-1 descriptor pointer
* point to the N-2 since they were
* actually merged.
*/
pdesc = ppdesc;

/*
* Rule out the case where we don't have
* pstride computed yet (our second sg
* element)
*
* We also want to catch the case where there
* would be a negative stride,
*/
} else if (pstride ||
sg_dma_address(sg) < sg_dma_address(psg)) {
/*
* Queue the N-1 descriptor after the
* N-2
*/
at_xdmac_queue_desc(chan, ppdesc, pdesc);

/*
* Add the N-1 descriptor to the list
* of the descriptors used for this
* transfer
*/
list_add_tail(&desc->desc_node,
&first->descs_list);
dev_dbg(chan2dev(chan),
"%s: add desc 0x%p to descs_list 0x%p\n",
__func__, desc, first);
}
}

/*
* If we are the last element, just see if we have the
* same size than the previous element.
*
* If so, we can merge it with the previous descriptor
* since we don't care about the stride anymore.
*/
if ((i == (sg_len - 1)) &&
sg_dma_len(ppsg) == sg_dma_len(psg)) {
dev_dbg(chan2dev(chan),
"%s: desc 0x%p can be merged with desc 0x%p\n",
__func__, desc, pdesc);

/*
* Increment the block count of the N-1
* descriptor
*/
at_xdmac_increment_block_count(chan, pdesc);
pdesc->lld.mbr_dus = stride;

/*
* Put back the N descriptor in the free
* descriptor list
*/
list_add_tail(&desc->desc_node,
&atchan->free_descs_list);
}

/* Update our descriptors */
ppdesc = pdesc;
pdesc = desc;

/* Update our scatter pointers */
ppsg = psg;
psg = sg;

len += sg_dma_len(sg);
}

first->tx_dma_desc.cookie = -EBUSY;
first->tx_dma_desc.flags = flags;
first->xfer_size = len;

return &first->tx_dma_desc;
}

static enum dma_status
at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
Expand Down Expand Up @@ -1734,6 +1896,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
dma_cap_set(DMA_INTERLEAVE, atxdmac->dma.cap_mask);
dma_cap_set(DMA_MEMCPY, atxdmac->dma.cap_mask);
dma_cap_set(DMA_MEMSET, atxdmac->dma.cap_mask);
dma_cap_set(DMA_MEMSET_SG, atxdmac->dma.cap_mask);
dma_cap_set(DMA_SLAVE, atxdmac->dma.cap_mask);
/*
* Without DMA_PRIVATE the driver is not able to allocate more than
Expand All @@ -1749,6 +1912,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
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_dma_memset = at_xdmac_prep_dma_memset;
atxdmac->dma.device_prep_dma_memset_sg = at_xdmac_prep_dma_memset_sg;
atxdmac->dma.device_prep_slave_sg = at_xdmac_prep_slave_sg;
atxdmac->dma.device_config = at_xdmac_device_config;
atxdmac->dma.device_pause = at_xdmac_device_pause;
Expand Down

0 comments on commit 67a6eed

Please sign in to comment.