linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support
@ 2015-05-27 14:01 Maxime Ripard
  2015-05-27 14:01 ` [PATCH 1/2] dmaengine: Move icg helpers to global header Maxime Ripard
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Maxime Ripard @ 2015-05-27 14:01 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, Nicolas Ferre, Alexandre Belloni, Ludovic Desroches,
	Thomas Petazzoni, linux-kernel, linux-arm-kernel, Maxime Ripard

Hi everyone,

This patchset implements the interleaved transfer capability found on
some AT91 SoCs.

The hardware actually support a somewhat similar mode called
picture-in-picture, that allows to transfer a squared portion of a
framebuffer.

That actually means that the interleaved transfer that this hardware
support will need to have the same size and ICGs across all its
chunks.

It has been tested on the SAMA5D36.

Let me know what you think!
Maxime

Maxime Ripard (2):
  dmaengine: Move icg helpers to global header
  dmaengine: hdmac: Implement interleaved transfers

 drivers/dma/at_hdmac.c      | 106 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/at_hdmac_regs.h |   5 +++
 drivers/dma/at_xdmac.c      |  46 ++-----------------
 include/linux/dmaengine.h   |  27 +++++++++++
 4 files changed, 142 insertions(+), 42 deletions(-)

-- 
2.4.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] dmaengine: Move icg helpers to global header
  2015-05-27 14:01 [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Maxime Ripard
@ 2015-05-27 14:01 ` Maxime Ripard
  2015-06-01  8:14   ` Ludovic Desroches
  2015-05-27 14:01 ` [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers Maxime Ripard
  2015-06-12 12:44 ` [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Vinod Koul
  2 siblings, 1 reply; 7+ messages in thread
From: Maxime Ripard @ 2015-05-27 14:01 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, Nicolas Ferre, Alexandre Belloni, Ludovic Desroches,
	Thomas Petazzoni, linux-kernel, linux-arm-kernel, Maxime Ripard

Now that we can have ICGs set for both the source and destination (using
the icg field of struct data_chunk) or for only the source or the
destination (using the dst_icg or src_icg respectively), and that these
fields can be ignored depending on other parameters (src_inc, src_sgl,
etc.), the logic to get the actual ICG value can be quite tricky.

The XDMAC driver was already implementing it, but since we will need it in
other drivers, we can move it to the main header file.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/dma/at_xdmac.c    | 46 ++++------------------------------------------
 include/linux/dmaengine.h | 27 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 42 deletions(-)

diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 9b602a67d40d..80e46e571bdd 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -862,20 +862,8 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
 
 	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_sus = dmaengine_get_src_icg(xt, chunk);
+	desc->lld.mbr_dus = dmaengine_get_dst_icg(xt, chunk);
 
 	desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3
 		| AT_XDMAC_MBR_UBC_NDEN
@@ -895,32 +883,6 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
 	return desc;
 }
 
-static size_t at_xdmac_get_icg(bool inc, bool sgl, size_t icg, size_t dir_icg)
-{
-	if (inc) {
-		if (dir_icg)
-			return dir_icg;
-		else if (sgl)
-			return icg;
-	}
-
-	return 0;
-}
-
-static size_t at_xdmac_get_dst_icg(struct dma_interleaved_template *xt,
-				   struct data_chunk *chunk)
-{
-	return at_xdmac_get_icg(xt->dst_inc, xt->dst_sgl,
-				chunk->icg, chunk->dst_icg);
-}
-
-static size_t at_xdmac_get_src_icg(struct dma_interleaved_template *xt,
-				   struct data_chunk *chunk)
-{
-	return at_xdmac_get_icg(xt->src_inc, xt->src_sgl,
-				chunk->icg, chunk->src_icg);
-}
-
 static struct dma_async_tx_descriptor *
 at_xdmac_prep_interleaved(struct dma_chan *chan,
 			  struct dma_interleaved_template *xt,
@@ -950,8 +912,8 @@ at_xdmac_prep_interleaved(struct dma_chan *chan,
 
 		chunk = xt->sgl + i;
 
-		dst_icg = at_xdmac_get_dst_icg(xt, chunk);
-		src_icg = at_xdmac_get_src_icg(xt, chunk);
+		dst_icg = dmaengine_get_dst_icg(xt, chunk);
+		src_icg = dmaengine_get_src_icg(xt, chunk);
 
 		src_skip = chunk->size + src_icg;
 		dst_skip = chunk->size + dst_icg;
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 8faf6a2dd9d8..43a9a397b2d7 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -899,6 +899,33 @@ static inline int dma_maxpq(struct dma_device *dma, enum dma_ctrl_flags flags)
 	BUG();
 }
 
+static inline size_t dmaengine_get_icg(bool inc, bool sgl, size_t icg,
+				      size_t dir_icg)
+{
+	if (inc) {
+		if (dir_icg)
+			return dir_icg;
+		else if (sgl)
+			return icg;
+	}
+
+	return 0;
+}
+
+static inline size_t dmaengine_get_dst_icg(struct dma_interleaved_template *xt,
+					   struct data_chunk *chunk)
+{
+	return dmaengine_get_icg(xt->dst_inc, xt->dst_sgl,
+				 chunk->icg, chunk->dst_icg);
+}
+
+static inline size_t dmaengine_get_src_icg(struct dma_interleaved_template *xt,
+					   struct data_chunk *chunk)
+{
+	return dmaengine_get_icg(xt->src_inc, xt->src_sgl,
+				 chunk->icg, chunk->src_icg);
+}
+
 /* --- public DMA engine API --- */
 
 #ifdef CONFIG_DMA_ENGINE
-- 
2.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers
  2015-05-27 14:01 [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Maxime Ripard
  2015-05-27 14:01 ` [PATCH 1/2] dmaengine: Move icg helpers to global header Maxime Ripard
@ 2015-05-27 14:01 ` Maxime Ripard
  2015-06-01  8:32   ` Ludovic Desroches
  2015-06-12 12:44 ` [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Vinod Koul
  2 siblings, 1 reply; 7+ messages in thread
From: Maxime Ripard @ 2015-05-27 14:01 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dmaengine, Nicolas Ferre, Alexandre Belloni, Ludovic Desroches,
	Thomas Petazzoni, linux-kernel, linux-arm-kernel, Maxime Ripard

The AT91 HDMAC controller supports interleaved transfers through what's
called the Picture-in-Picture mode, which allows to transfer a squared
portion of a framebuffer.

This means that this interleaved transfer only supports interleaved
transfers which have a transfer size and ICGs that are fixed across all the
chunks.

While this is a quite drastic restriction of the interleaved transfers
compared to what the dmaengine API allows, this is still useful, and our
driver will only reject transfers that do not conform to this.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/dma/at_hdmac.c      | 106 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/at_hdmac_regs.h |   5 +++
 2 files changed, 111 insertions(+)

diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 57b2141ddddc..59892126d175 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -247,6 +247,10 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first)
 	channel_writel(atchan, CTRLA, 0);
 	channel_writel(atchan, CTRLB, 0);
 	channel_writel(atchan, DSCR, first->txd.phys);
+	channel_writel(atchan, SPIP, ATC_SPIP_HOLE(first->src_hole) |
+		       ATC_SPIP_BOUNDARY(first->boundary));
+	channel_writel(atchan, DPIP, ATC_DPIP_HOLE(first->dst_hole) |
+		       ATC_DPIP_BOUNDARY(first->boundary));
 	dma_writel(atdma, CHER, atchan->mask);
 
 	vdbg_dump_regs(atchan);
@@ -635,6 +639,104 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
 }
 
 /**
+ * atc_prep_dma_interleaved - prepare memory to memory interleaved operation
+ * @chan: the channel to prepare operation on
+ * @xt: Interleaved transfer template
+ * @flags: tx descriptor status flags
+ */
+static struct dma_async_tx_descriptor *
+atc_prep_dma_interleaved(struct dma_chan *chan,
+			 struct dma_interleaved_template *xt,
+			 unsigned long flags)
+{
+	struct at_dma_chan	*atchan = to_at_dma_chan(chan);
+	struct data_chunk	*first = xt->sgl;
+	struct at_desc		*desc = NULL;
+	size_t			xfer_count;
+	unsigned int		dwidth;
+	u32			ctrla;
+	u32			ctrlb;
+	size_t			len = 0;
+	int			i;
+
+	dev_info(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);
+
+	if (unlikely(!xt || xt->numf != 1 || !xt->frame_size))
+		return NULL;
+
+	/*
+	 * The controller can only "skip" X bytes every Y bytes, so we
+	 * need to make sure we are given a template that fit that
+	 * description, ie a template with chunks that always have the
+	 * same size, with the same ICGs.
+	 */
+	for (i = 0; i < xt->frame_size; i++) {
+		struct data_chunk *chunk = xt->sgl + i;
+
+		if ((chunk->size != xt->sgl->size) ||
+		    (dmaengine_get_dst_icg(xt, chunk) != dmaengine_get_dst_icg(xt, first)) ||
+		    (dmaengine_get_src_icg(xt, chunk) != dmaengine_get_src_icg(xt, first))) {
+			dev_err(chan2dev(chan),
+				"%s: the controller can transfer only identical chunks\n",
+				__func__);
+			return NULL;
+		}
+
+		len += chunk->size;
+	}
+
+	dwidth = atc_get_xfer_width(xt->src_start,
+				    xt->dst_start, len);
+
+	xfer_count = len >> dwidth;
+	if (xfer_count > ATC_BTSIZE_MAX) {
+		dev_err(chan2dev(chan), "%s: buffer is too big\n", __func__);
+		return NULL;
+	}
+
+	ctrla = ATC_SRC_WIDTH(dwidth) |
+		ATC_DST_WIDTH(dwidth);
+
+	ctrlb =   ATC_DEFAULT_CTRLB | ATC_IEN
+		| ATC_SRC_ADDR_MODE_INCR
+		| ATC_DST_ADDR_MODE_INCR
+		| ATC_SRC_PIP
+		| ATC_DST_PIP
+		| ATC_FC_MEM2MEM;
+
+	/* create the transfer */
+	desc = atc_desc_get(atchan);
+	if (!desc) {
+		dev_err(chan2dev(chan),
+			"%s: couldn't allocate our descriptor\n", __func__);
+		return NULL;
+	}
+
+	desc->lli.saddr = xt->src_start;
+	desc->lli.daddr = xt->dst_start;
+	desc->lli.ctrla = ctrla | xfer_count;
+	desc->lli.ctrlb = ctrlb;
+
+	desc->boundary = first->size >> dwidth;
+	desc->dst_hole = (dmaengine_get_dst_icg(xt, first) >> dwidth) + 1;
+	desc->src_hole = (dmaengine_get_src_icg(xt, first) >> dwidth) + 1;
+
+	desc->txd.cookie = -EBUSY;
+	desc->total_len = desc->len = len;
+	desc->tx_width = dwidth;
+
+	/* set end-of-link to the last link descriptor of list*/
+	set_desc_eol(desc);
+
+	desc->txd.flags = flags; /* client is in control of this ack */
+
+	return &desc->txd;
+}
+
+/**
  * atc_prep_dma_memcpy - prepare a memcpy operation
  * @chan: the channel to prepare operation on
  * @dest: operation virtual destination address
@@ -1609,6 +1711,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
 	/* setup platform data for each SoC */
 	dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask);
 	dma_cap_set(DMA_SG, at91sam9rl_config.cap_mask);
+	dma_cap_set(DMA_INTERLEAVE, at91sam9g45_config.cap_mask);
 	dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask);
 	dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask);
 	dma_cap_set(DMA_SG, at91sam9g45_config.cap_mask);
@@ -1713,6 +1816,9 @@ static int __init at_dma_probe(struct platform_device *pdev)
 	atdma->dma_common.dev = &pdev->dev;
 
 	/* set prep routines based on capability */
+	if (dma_has_cap(DMA_INTERLEAVE, atdma->dma_common.cap_mask))
+		atdma->dma_common.device_prep_interleaved_dma = atc_prep_dma_interleaved;
+
 	if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask))
 		atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy;
 
diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
index 2727ca560572..bc8d5ebedd19 100644
--- a/drivers/dma/at_hdmac_regs.h
+++ b/drivers/dma/at_hdmac_regs.h
@@ -196,6 +196,11 @@ struct at_desc {
 	size_t				len;
 	u32				tx_width;
 	size_t				total_len;
+
+	/* Interleaved data */
+	size_t				boundary;
+	size_t				dst_hole;
+	size_t				src_hole;
 };
 
 static inline struct at_desc *
-- 
2.4.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] dmaengine: Move icg helpers to global header
  2015-05-27 14:01 ` [PATCH 1/2] dmaengine: Move icg helpers to global header Maxime Ripard
@ 2015-06-01  8:14   ` Ludovic Desroches
  0 siblings, 0 replies; 7+ messages in thread
From: Ludovic Desroches @ 2015-06-01  8:14 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vinod Koul, dmaengine, Nicolas Ferre, Alexandre Belloni,
	Ludovic Desroches, Thomas Petazzoni, linux-kernel,
	linux-arm-kernel

On Wed, May 27, 2015 at 04:01:52PM +0200, Maxime Ripard wrote:
> Now that we can have ICGs set for both the source and destination (using
> the icg field of struct data_chunk) or for only the source or the
> destination (using the dst_icg or src_icg respectively), and that these
> fields can be ignored depending on other parameters (src_inc, src_sgl,
> etc.), the logic to get the actual ICG value can be quite tricky.
> 
> The XDMAC driver was already implementing it, but since we will need it in
> other drivers, we can move it to the main header file.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com>

> ---
>  drivers/dma/at_xdmac.c    | 46 ++++------------------------------------------
>  include/linux/dmaengine.h | 27 +++++++++++++++++++++++++++
>  2 files changed, 31 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
> index 9b602a67d40d..80e46e571bdd 100644
> --- a/drivers/dma/at_xdmac.c
> +++ b/drivers/dma/at_xdmac.c
> @@ -862,20 +862,8 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
>  
>  	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_sus = dmaengine_get_src_icg(xt, chunk);
> +	desc->lld.mbr_dus = dmaengine_get_dst_icg(xt, chunk);
>  
>  	desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3
>  		| AT_XDMAC_MBR_UBC_NDEN
> @@ -895,32 +883,6 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
>  	return desc;
>  }
>  
> -static size_t at_xdmac_get_icg(bool inc, bool sgl, size_t icg, size_t dir_icg)
> -{
> -	if (inc) {
> -		if (dir_icg)
> -			return dir_icg;
> -		else if (sgl)
> -			return icg;
> -	}
> -
> -	return 0;
> -}
> -
> -static size_t at_xdmac_get_dst_icg(struct dma_interleaved_template *xt,
> -				   struct data_chunk *chunk)
> -{
> -	return at_xdmac_get_icg(xt->dst_inc, xt->dst_sgl,
> -				chunk->icg, chunk->dst_icg);
> -}
> -
> -static size_t at_xdmac_get_src_icg(struct dma_interleaved_template *xt,
> -				   struct data_chunk *chunk)
> -{
> -	return at_xdmac_get_icg(xt->src_inc, xt->src_sgl,
> -				chunk->icg, chunk->src_icg);
> -}
> -
>  static struct dma_async_tx_descriptor *
>  at_xdmac_prep_interleaved(struct dma_chan *chan,
>  			  struct dma_interleaved_template *xt,
> @@ -950,8 +912,8 @@ at_xdmac_prep_interleaved(struct dma_chan *chan,
>  
>  		chunk = xt->sgl + i;
>  
> -		dst_icg = at_xdmac_get_dst_icg(xt, chunk);
> -		src_icg = at_xdmac_get_src_icg(xt, chunk);
> +		dst_icg = dmaengine_get_dst_icg(xt, chunk);
> +		src_icg = dmaengine_get_src_icg(xt, chunk);
>  
>  		src_skip = chunk->size + src_icg;
>  		dst_skip = chunk->size + dst_icg;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 8faf6a2dd9d8..43a9a397b2d7 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -899,6 +899,33 @@ static inline int dma_maxpq(struct dma_device *dma, enum dma_ctrl_flags flags)
>  	BUG();
>  }
>  
> +static inline size_t dmaengine_get_icg(bool inc, bool sgl, size_t icg,
> +				      size_t dir_icg)
> +{
> +	if (inc) {
> +		if (dir_icg)
> +			return dir_icg;
> +		else if (sgl)
> +			return icg;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline size_t dmaengine_get_dst_icg(struct dma_interleaved_template *xt,
> +					   struct data_chunk *chunk)
> +{
> +	return dmaengine_get_icg(xt->dst_inc, xt->dst_sgl,
> +				 chunk->icg, chunk->dst_icg);
> +}
> +
> +static inline size_t dmaengine_get_src_icg(struct dma_interleaved_template *xt,
> +					   struct data_chunk *chunk)
> +{
> +	return dmaengine_get_icg(xt->src_inc, xt->src_sgl,
> +				 chunk->icg, chunk->src_icg);
> +}
> +
>  /* --- public DMA engine API --- */
>  
>  #ifdef CONFIG_DMA_ENGINE
> -- 
> 2.4.1
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers
  2015-05-27 14:01 ` [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers Maxime Ripard
@ 2015-06-01  8:32   ` Ludovic Desroches
  2015-06-02  9:19     ` Maxime Ripard
  0 siblings, 1 reply; 7+ messages in thread
From: Ludovic Desroches @ 2015-06-01  8:32 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Vinod Koul, dmaengine, Nicolas Ferre, Alexandre Belloni,
	Ludovic Desroches, Thomas Petazzoni, linux-kernel,
	linux-arm-kernel

On Wed, May 27, 2015 at 04:01:53PM +0200, Maxime Ripard wrote:
> The AT91 HDMAC controller supports interleaved transfers through what's
> called the Picture-in-Picture mode, which allows to transfer a squared
> portion of a framebuffer.
> 
> This means that this interleaved transfer only supports interleaved
> transfers which have a transfer size and ICGs that are fixed across all the
> chunks.
> 
> While this is a quite drastic restriction of the interleaved transfers
> compared to what the dmaengine API allows, this is still useful, and our
> driver will only reject transfers that do not conform to this.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
One comment below, otherwise

Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com>

> ---
>  drivers/dma/at_hdmac.c      | 106 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/dma/at_hdmac_regs.h |   5 +++
>  2 files changed, 111 insertions(+)
> 
> diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
> index 57b2141ddddc..59892126d175 100644
> --- a/drivers/dma/at_hdmac.c
> +++ b/drivers/dma/at_hdmac.c
> @@ -247,6 +247,10 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first)
>  	channel_writel(atchan, CTRLA, 0);
>  	channel_writel(atchan, CTRLB, 0);
>  	channel_writel(atchan, DSCR, first->txd.phys);
> +	channel_writel(atchan, SPIP, ATC_SPIP_HOLE(first->src_hole) |
> +		       ATC_SPIP_BOUNDARY(first->boundary));
> +	channel_writel(atchan, DPIP, ATC_DPIP_HOLE(first->dst_hole) |
> +		       ATC_DPIP_BOUNDARY(first->boundary));

Can't we get a trivial condition to perform these two writes only if
needed?

>  	dma_writel(atdma, CHER, atchan->mask);
>  
>  	vdbg_dump_regs(atchan);
> @@ -635,6 +639,104 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx)
>  }
>  
>  /**
> + * atc_prep_dma_interleaved - prepare memory to memory interleaved operation
> + * @chan: the channel to prepare operation on
> + * @xt: Interleaved transfer template
> + * @flags: tx descriptor status flags
> + */
> +static struct dma_async_tx_descriptor *
> +atc_prep_dma_interleaved(struct dma_chan *chan,
> +			 struct dma_interleaved_template *xt,
> +			 unsigned long flags)
> +{
> +	struct at_dma_chan	*atchan = to_at_dma_chan(chan);
> +	struct data_chunk	*first = xt->sgl;
> +	struct at_desc		*desc = NULL;
> +	size_t			xfer_count;
> +	unsigned int		dwidth;
> +	u32			ctrla;
> +	u32			ctrlb;
> +	size_t			len = 0;
> +	int			i;
> +
> +	dev_info(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);
> +
> +	if (unlikely(!xt || xt->numf != 1 || !xt->frame_size))
> +		return NULL;
> +
> +	/*
> +	 * The controller can only "skip" X bytes every Y bytes, so we
> +	 * need to make sure we are given a template that fit that
> +	 * description, ie a template with chunks that always have the
> +	 * same size, with the same ICGs.
> +	 */
> +	for (i = 0; i < xt->frame_size; i++) {
> +		struct data_chunk *chunk = xt->sgl + i;
> +
> +		if ((chunk->size != xt->sgl->size) ||
> +		    (dmaengine_get_dst_icg(xt, chunk) != dmaengine_get_dst_icg(xt, first)) ||
> +		    (dmaengine_get_src_icg(xt, chunk) != dmaengine_get_src_icg(xt, first))) {
> +			dev_err(chan2dev(chan),
> +				"%s: the controller can transfer only identical chunks\n",
> +				__func__);
> +			return NULL;
> +		}
> +
> +		len += chunk->size;
> +	}
> +
> +	dwidth = atc_get_xfer_width(xt->src_start,
> +				    xt->dst_start, len);
> +
> +	xfer_count = len >> dwidth;
> +	if (xfer_count > ATC_BTSIZE_MAX) {
> +		dev_err(chan2dev(chan), "%s: buffer is too big\n", __func__);
> +		return NULL;
> +	}
> +
> +	ctrla = ATC_SRC_WIDTH(dwidth) |
> +		ATC_DST_WIDTH(dwidth);
> +
> +	ctrlb =   ATC_DEFAULT_CTRLB | ATC_IEN
> +		| ATC_SRC_ADDR_MODE_INCR
> +		| ATC_DST_ADDR_MODE_INCR
> +		| ATC_SRC_PIP
> +		| ATC_DST_PIP
> +		| ATC_FC_MEM2MEM;
> +
> +	/* create the transfer */
> +	desc = atc_desc_get(atchan);
> +	if (!desc) {
> +		dev_err(chan2dev(chan),
> +			"%s: couldn't allocate our descriptor\n", __func__);
> +		return NULL;
> +	}
> +
> +	desc->lli.saddr = xt->src_start;
> +	desc->lli.daddr = xt->dst_start;
> +	desc->lli.ctrla = ctrla | xfer_count;
> +	desc->lli.ctrlb = ctrlb;
> +
> +	desc->boundary = first->size >> dwidth;
> +	desc->dst_hole = (dmaengine_get_dst_icg(xt, first) >> dwidth) + 1;
> +	desc->src_hole = (dmaengine_get_src_icg(xt, first) >> dwidth) + 1;
> +
> +	desc->txd.cookie = -EBUSY;
> +	desc->total_len = desc->len = len;
> +	desc->tx_width = dwidth;
> +
> +	/* set end-of-link to the last link descriptor of list*/
> +	set_desc_eol(desc);
> +
> +	desc->txd.flags = flags; /* client is in control of this ack */
> +
> +	return &desc->txd;
> +}
> +
> +/**
>   * atc_prep_dma_memcpy - prepare a memcpy operation
>   * @chan: the channel to prepare operation on
>   * @dest: operation virtual destination address
> @@ -1609,6 +1711,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
>  	/* setup platform data for each SoC */
>  	dma_cap_set(DMA_MEMCPY, at91sam9rl_config.cap_mask);
>  	dma_cap_set(DMA_SG, at91sam9rl_config.cap_mask);
> +	dma_cap_set(DMA_INTERLEAVE, at91sam9g45_config.cap_mask);
>  	dma_cap_set(DMA_MEMCPY, at91sam9g45_config.cap_mask);
>  	dma_cap_set(DMA_SLAVE, at91sam9g45_config.cap_mask);
>  	dma_cap_set(DMA_SG, at91sam9g45_config.cap_mask);
> @@ -1713,6 +1816,9 @@ static int __init at_dma_probe(struct platform_device *pdev)
>  	atdma->dma_common.dev = &pdev->dev;
>  
>  	/* set prep routines based on capability */
> +	if (dma_has_cap(DMA_INTERLEAVE, atdma->dma_common.cap_mask))
> +		atdma->dma_common.device_prep_interleaved_dma = atc_prep_dma_interleaved;
> +
>  	if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask))
>  		atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy;
>  
> diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h
> index 2727ca560572..bc8d5ebedd19 100644
> --- a/drivers/dma/at_hdmac_regs.h
> +++ b/drivers/dma/at_hdmac_regs.h
> @@ -196,6 +196,11 @@ struct at_desc {
>  	size_t				len;
>  	u32				tx_width;
>  	size_t				total_len;
> +
> +	/* Interleaved data */
> +	size_t				boundary;
> +	size_t				dst_hole;
> +	size_t				src_hole;
>  };
>  
>  static inline struct at_desc *
> -- 
> 2.4.1
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers
  2015-06-01  8:32   ` Ludovic Desroches
@ 2015-06-02  9:19     ` Maxime Ripard
  0 siblings, 0 replies; 7+ messages in thread
From: Maxime Ripard @ 2015-06-02  9:19 UTC (permalink / raw)
  To: Vinod Koul, dmaengine, Nicolas Ferre, Alexandre Belloni,
	Thomas Petazzoni, linux-kernel, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 851 bytes --]

Hi,

On Mon, Jun 01, 2015 at 10:32:16AM +0200, Ludovic Desroches wrote:
> >  	channel_writel(atchan, CTRLA, 0);
> >  	channel_writel(atchan, CTRLB, 0);
> >  	channel_writel(atchan, DSCR, first->txd.phys);
> > +	channel_writel(atchan, SPIP, ATC_SPIP_HOLE(first->src_hole) |
> > +		       ATC_SPIP_BOUNDARY(first->boundary));
> > +	channel_writel(atchan, DPIP, ATC_DPIP_HOLE(first->dst_hole) |
> > +		       ATC_DPIP_BOUNDARY(first->boundary));
> 
> Can't we get a trivial condition to perform these two writes only if
> needed?

Is that really needed?

My understanding is that the hardware will only take this into account
if we set the ATC_SRC_PIP and ATC_DST_PIP bits anyway, so the check is
already there.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support
  2015-05-27 14:01 [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Maxime Ripard
  2015-05-27 14:01 ` [PATCH 1/2] dmaengine: Move icg helpers to global header Maxime Ripard
  2015-05-27 14:01 ` [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers Maxime Ripard
@ 2015-06-12 12:44 ` Vinod Koul
  2 siblings, 0 replies; 7+ messages in thread
From: Vinod Koul @ 2015-06-12 12:44 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: dmaengine, Nicolas Ferre, Alexandre Belloni, Ludovic Desroches,
	Thomas Petazzoni, linux-kernel, linux-arm-kernel

On Wed, May 27, 2015 at 04:01:51PM +0200, Maxime Ripard wrote:
> Hi everyone,
> 
> This patchset implements the interleaved transfer capability found on
> some AT91 SoCs.
> 
> The hardware actually support a somewhat similar mode called
> picture-in-picture, that allows to transfer a squared portion of a
> framebuffer.
> 
> That actually means that the interleaved transfer that this hardware
> support will need to have the same size and ICGs across all its
> chunks.
> 
> It has been tested on the SAMA5D36.

Applied, thanks

-- 
~Vinod


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-06-12 12:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-27 14:01 [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Maxime Ripard
2015-05-27 14:01 ` [PATCH 1/2] dmaengine: Move icg helpers to global header Maxime Ripard
2015-06-01  8:14   ` Ludovic Desroches
2015-05-27 14:01 ` [PATCH 2/2] dmaengine: hdmac: Implement interleaved transfers Maxime Ripard
2015-06-01  8:32   ` Ludovic Desroches
2015-06-02  9:19     ` Maxime Ripard
2015-06-12 12:44 ` [PATCH 0/2] dmaengine: hdmac: Implement interleaved transfers support Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).