[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20150716120154.GN5086@localhost>
Date: Thu, 16 Jul 2015 17:31:54 +0530
From: Vinod Koul <vinod.koul@...el.com>
To: Maxime Ripard <maxime.ripard@...e-electrons.com>
Cc: Nicolas Ferre <nicolas.ferre@...el.com>,
Alexandre Belloni <alexandre.belloni@...e-electrons.com>,
Ludovic Desroches <ludovic.desroches@...el.com>,
Thomas Petazzoni <thomas.petazzoni@...e-electrons.com>,
Boris Brezillon <boris.brezillon@...e-electrons.com>,
dmaengine@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org
Subject: Re: [PATCH 2/2] dmaengine: xdmac: Add scatter gathered memset support
On Mon, Jul 06, 2015 at 12:19:24PM +0200, Maxime Ripard wrote:
> The XDMAC also supports memset operations over discontiguous areas. Add the
> necessary logic to support this.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@...e-electrons.com>
> ---
> drivers/dma/at_xdmac.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 165 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
> index cf1213de7865..1a2d9a39ff25 100644
> --- a/drivers/dma/at_xdmac.c
> +++ b/drivers/dma/at_xdmac.c
> @@ -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)
> @@ -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.
> + */
Why do you need stride?
This is scatterlist so the computation of stride sounds odd here. Ideally
you should take the scatterlist and program the lli for controller.
--
~Vinod
> + 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)
> @@ -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
> @@ -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;
> --
> 2.4.5
>
--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists