diff mbox

[2/4] dmaengine: vdma: Add support for mulit-channel dma mode

Message ID 1465549954-30220-3-git-send-email-appanad@xilinx.com (mailing list archive)
State New, archived
Headers show

Commit Message

Appana Durga Kedareswara rao June 10, 2016, 9:12 a.m. UTC
This patch adds support for AXI DMA multi-channel dma mode
Multichannel mode enables DMA to connect to multiple masters
And slaves on the streaming side.
In Multichannel mode AXI DMA supports 2D transfers.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
---
 drivers/dma/xilinx/xilinx_vdma.c |  242 ++++++++++++++++++++++++++++++++++----
 include/linux/dma/xilinx_dma.h   |   18 +++
 2 files changed, 237 insertions(+), 23 deletions(-)

Comments

Vinod Koul June 21, 2016, 3:55 p.m. UTC | #1
On Fri, Jun 10, 2016 at 02:42:32PM +0530, Kedareswara rao Appana wrote:
> This patch adds support for AXI DMA multi-channel dma mode
> Multichannel mode enables DMA to connect to multiple masters
> And slaves on the streaming side.
> In Multichannel mode AXI DMA supports 2D transfers.

Funny formatting!

Can you elobrate what you meant by Multichannel mode? This patch seems to do
two things, one is to add interleaved dma support and something else. Can
you explain the latter part?

>  /**
> + * struct xilinx_mcdma_config - DMA Multi channel configuration structure
> + * @tdest: Channel to operate on
> + * @tid:   Channel configuration
> + * @tuser: Tuser configuration
> + * @ax_user: ax_user value
> + * @ax_cache: ax_cache value
> + */
> +struct xilinx_mcdma_config {
> +	u8 tdest;
> +	u8 tid;
> +	u8 tuser;
> +	u8 ax_user;
> +	u8 ax_cache;

can you describe these in details, what do these do, what are the values to
be programmed?
Appana Durga Kedareswara rao June 21, 2016, 4:02 p.m. UTC | #2
Hi Vinod,

	Thanks for the review...

> 
> On Fri, Jun 10, 2016 at 02:42:32PM +0530, Kedareswara rao Appana wrote:
> > This patch adds support for AXI DMA multi-channel dma mode
> > Multichannel mode enables DMA to connect to multiple masters And
> > slaves on the streaming side.
> > In Multichannel mode AXI DMA supports 2D transfers.
> 
> Funny formatting!

Will fix in next version...

> 
> Can you elobrate what you meant by Multichannel mode? This patch seems to
> do two things, one is to add interleaved dma support and something else. Can
> you explain the latter part?

AXI DMA has two Stream interfaces (Memory to Stream MM2S and Stream to Memory S2MM)
In Multi-Channel dma mode each stream interface can be configured up to 16 channels.
In Multi-channel DMA mode IP supports only interleaved transfers (2-D transfers).

> 
> >  /**
> > + * struct xilinx_mcdma_config - DMA Multi channel configuration
> > +structure
> > + * @tdest: Channel to operate on
> > + * @tid:   Channel configuration
> > + * @tuser: Tuser configuration
> > + * @ax_user: ax_user value
> > + * @ax_cache: ax_cache value
> > + */
> > +struct xilinx_mcdma_config {
> > +	u8 tdest;
> > +	u8 tid;
> > +	u8 tuser;
> > +	u8 ax_user;
> > +	u8 ax_cache;
> 
> can you describe these in details, what do these do, what are the values to be
> programmed?

As said above In Multi-Channel Mode each Stream interface can be 
Configured up to 16 channels each channel is differentiated based on the tdest and tid values.

tdest:
TDEST provides routing information for the data stream.
TDEST values are static for the entire packet.

tid:
Provides a stream identifier. TID values are static for entire packet.
TID values provided in the TX descriptor field are presented on
TID signals of the streaming side.

tuser:
Sideband signals used for user-defined information.
TUSER values are static for entire packet. TUSER values provided in the TX
Descriptor field are presented on TUSER signals of streaming side.

ax_user: 
Sideband signals used for user-defined information
ARUSER values and their interpretations are user-defined

ax_cache:
Cache type this signal provides additional information about the cacheable
Characteristics of the transfer.

In the above parameters tdest and tid are mandatory to differentiate b/w different channels
Other parameters are optional please let me know if you don't want me to 
Include other optional parameters will remove in the next version.

Regards,
Kedar.

> 
> --
> ~Vinod
Vinod Koul June 21, 2016, 4:33 p.m. UTC | #3
On Tue, Jun 21, 2016 at 04:02:05PM +0000, Appana Durga Kedareswara Rao wrote:
> Hi Vinod,
> 
> 	Thanks for the review...
> 
> > 
> > On Fri, Jun 10, 2016 at 02:42:32PM +0530, Kedareswara rao Appana wrote:
> > > This patch adds support for AXI DMA multi-channel dma mode
> > > Multichannel mode enables DMA to connect to multiple masters And
> > > slaves on the streaming side.
> > > In Multichannel mode AXI DMA supports 2D transfers.
> > 
> > Funny formatting!
> 
> Will fix in next version...
> 
> > 
> > Can you elobrate what you meant by Multichannel mode? This patch seems to
> > do two things, one is to add interleaved dma support and something else. Can
> > you explain the latter part?
> 
> AXI DMA has two Stream interfaces (Memory to Stream MM2S and Stream to Memory S2MM)

what is a stream in this context?

> In Multi-Channel dma mode each stream interface can be configured up to 16 channels.
> In Multi-channel DMA mode IP supports only interleaved transfers (2-D transfers).


> 
> > 
> > >  /**
> > > + * struct xilinx_mcdma_config - DMA Multi channel configuration
> > > +structure
> > > + * @tdest: Channel to operate on
> > > + * @tid:   Channel configuration
> > > + * @tuser: Tuser configuration
> > > + * @ax_user: ax_user value
> > > + * @ax_cache: ax_cache value
> > > + */
> > > +struct xilinx_mcdma_config {
> > > +	u8 tdest;
> > > +	u8 tid;
> > > +	u8 tuser;
> > > +	u8 ax_user;
> > > +	u8 ax_cache;
> > 
> > can you describe these in details, what do these do, what are the values to be
> > programmed?
> 
> As said above In Multi-Channel Mode each Stream interface can be 
> Configured up to 16 channels each channel is differentiated based on the tdest and tid values.

Then why are you not registering 16 channels for this? That should give you
channel to operate on!

> 
> tdest:
> TDEST provides routing information for the data stream.

pls elobrate

> TDEST values are static for the entire packet.

what do these mean

> 
> tid:
> Provides a stream identifier. TID values are static for entire packet.
> TID values provided in the TX descriptor field are presented on
> TID signals of the streaming side.

what is this used for?

> 
> tuser:
> Sideband signals used for user-defined information.
> TUSER values are static for entire packet. TUSER values provided in the TX
> Descriptor field are presented on TUSER signals of streaming side.

what is this used for?

> 
> ax_user: 
> Sideband signals used for user-defined information
> ARUSER values and their interpretations are user-defined

How is it used by client?

> 
> ax_cache:
> Cache type this signal provides additional information about the cacheable
> Characteristics of the transfer.

and what are those...

> 
> In the above parameters tdest and tid are mandatory to differentiate b/w different channels
> Other parameters are optional please let me know if you don't want me to 
> Include other optional parameters will remove in the next version.

dmaengine provides channel as an argument so it is quite odd that you need
to differentiate b/w different channels. This only makes me worry that
things are not standard and fishy
Appana Durga Kedareswara rao June 22, 2016, 7:04 a.m. UTC | #4
Hi Vinod,

> 
> On Tue, Jun 21, 2016 at 04:02:05PM +0000, Appana Durga Kedareswara Rao
> wrote:
> > Hi Vinod,
> >
> > 	Thanks for the review...
> >
> > >
> > > On Fri, Jun 10, 2016 at 02:42:32PM +0530, Kedareswara rao Appana wrote:
> > > > This patch adds support for AXI DMA multi-channel dma mode
> > > > Multichannel mode enables DMA to connect to multiple masters And
> > > > slaves on the streaming side.
> > > > In Multichannel mode AXI DMA supports 2D transfers.
> > >
> > > Funny formatting!
> >
> > Will fix in next version...
> >
> > >
> > > Can you elobrate what you meant by Multichannel mode? This patch
> > > seems to do two things, one is to add interleaved dma support and
> > > something else. Can you explain the latter part?
> >
> > AXI DMA has two Stream interfaces (Memory to Stream MM2S and Stream to
> > Memory S2MM)
> 
> what is a stream in this context?

Stream means I/O transfer (Memory to I/O and I/O to Memory).
Sorry if I confused you.

> 
> > In Multi-Channel dma mode each stream interface can be configured up to 16
> channels.
> > In Multi-channel DMA mode IP supports only interleaved transfers (2-D
> transfers).
> 
> 
> >
> > >
> > > >  /**
> > > > + * struct xilinx_mcdma_config - DMA Multi channel configuration
> > > > +structure
> > > > + * @tdest: Channel to operate on
> > > > + * @tid:   Channel configuration
> > > > + * @tuser: Tuser configuration
> > > > + * @ax_user: ax_user value
> > > > + * @ax_cache: ax_cache value
> > > > + */
> > > > +struct xilinx_mcdma_config {
> > > > +	u8 tdest;
> > > > +	u8 tid;
> > > > +	u8 tuser;
> > > > +	u8 ax_user;
> > > > +	u8 ax_cache;
> > >
> > > can you describe these in details, what do these do, what are the
> > > values to be programmed?
> >
> > As said above In Multi-Channel Mode each Stream interface can be
> > Configured up to 16 channels each channel is differentiated based on the tdest
> and tid values.
> 
> Then why are you not registering 16 channels for this? That should give you
> channel to operate on!

The number of channels are configurable.
We are registering number of Channels that h/w configured for.

Will fix in the next version. Will remove this config.
And based on the channel type will configure the h/w.

> 
> >
> > tdest:
> > TDEST provides routing information for the data stream.
> 
> pls elobrate

Need to configure this with the channel number that
We would like to transfer data.

> 
> > TDEST values are static for the entire packet.
> 
> what do these mean

We shouldn't modify this value when a transfer is going on...
Providing it in the config option user may not aware of all this possibilities.
Will remove this config and will handle this in the driver itself.

> 
> >
> > tid:
> > Provides a stream identifier. TID values are static for entire packet.
> > TID values provided in the TX descriptor field are presented on TID
> > signals of the streaming side.
> 
> what is this used for?

Currently I am not using this parameter will remove this config.
But usually it is used as a packet identifier.

> 
> >
> > tuser:
> > Sideband signals used for user-defined information.
> > TUSER values are static for entire packet. TUSER values provided in
> > the TX Descriptor field are presented on TUSER signals of streaming side.
> 
> what is this used for?

Used for user-defined information.
Currently I am not using it will remove this in the next version...

> 
> >
> > ax_user:
> > Sideband signals used for user-defined information ARUSER values and
> > their interpretations are user-defined
> 
> How is it used by client?

Currently I am not using it will remove this in the next version...

> 
> >
> > ax_cache:
> > Cache type this signal provides additional information about the
> > cacheable Characteristics of the transfer.
> 
> and what are those...

It is a 4-bit filed.
Bit 0: Bufferable
Bit 1: Modifiable
Bit 2: Non-cacheable
Bit 3: Normal

Currently I am not using it will remove this in the next version...

> 
> >
> > In the above parameters tdest and tid are mandatory to differentiate
> > b/w different channels Other parameters are optional please let me
> > know if you don't want me to Include other optional parameters will remove in
> the next version.
> 
> dmaengine provides channel as an argument so it is quite odd that you need to
> differentiate b/w different channels. This only makes me worry that things are
> not standard and fishy

Will fix in the next version will handle
Based on the channel type only.

Regards,
Kedar.

> 
> --
> ~Vinod
Vinod Koul June 28, 2016, 4:34 a.m. UTC | #5
On Wed, Jun 22, 2016 at 07:04:28AM +0000, Appana Durga Kedareswara Rao wrote:
> > > >
> > > > Can you elobrate what you meant by Multichannel mode? This patch
> > > > seems to do two things, one is to add interleaved dma support and
> > > > something else. Can you explain the latter part?
> > >
> > > AXI DMA has two Stream interfaces (Memory to Stream MM2S and Stream to
> > > Memory S2MM)
> > 
> > what is a stream in this context?
> 
> Stream means I/O transfer (Memory to I/O and I/O to Memory).
> Sorry if I confused you.
> 
> > 
> > > In Multi-Channel dma mode each stream interface can be configured up to 16
> > channels.
> > > In Multi-channel DMA mode IP supports only interleaved transfers (2-D
> > transfers).
> > 
> > 
> > >
> > > >
> > > > >  /**
> > > > > + * struct xilinx_mcdma_config - DMA Multi channel configuration
> > > > > +structure
> > > > > + * @tdest: Channel to operate on
> > > > > + * @tid:   Channel configuration
> > > > > + * @tuser: Tuser configuration
> > > > > + * @ax_user: ax_user value
> > > > > + * @ax_cache: ax_cache value
> > > > > + */
> > > > > +struct xilinx_mcdma_config {
> > > > > +	u8 tdest;
> > > > > +	u8 tid;
> > > > > +	u8 tuser;
> > > > > +	u8 ax_user;
> > > > > +	u8 ax_cache;
> > > >
> > > > can you describe these in details, what do these do, what are the
> > > > values to be programmed?
> > >
> > > As said above In Multi-Channel Mode each Stream interface can be
> > > Configured up to 16 channels each channel is differentiated based on the tdest
> > and tid values.
> > 
> > Then why are you not registering 16 channels for this? That should give you
> > channel to operate on!
> 
> The number of channels are configurable.
> We are registering number of Channels that h/w configured for.
> 
> Will fix in the next version. Will remove this config.
> And based on the channel type will configure the h/w.

Looking at this you should redesign!

The vchan was designed to operate on 'virtual' channels. The hardware
channels can be independent of that.

Your IP seems to be a good fit for that approach. Do not link the two and
separate them. User can have a virtual channel. In your driver, you can
manage hardware channels...

> 
> > 
> > >
> > > tdest:
> > > TDEST provides routing information for the data stream.
> > 
> > pls elobrate
> 
> Need to configure this with the channel number that
> We would like to transfer data.

This should be internal to driver...
Appana Durga Kedareswara rao June 28, 2016, 5:55 a.m. UTC | #6
Hi Vinod,

	Thanks for the review...

> > > > > >  /**
> > > > > > + * struct xilinx_mcdma_config - DMA Multi channel
> > > > > > +configuration structure
> > > > > > + * @tdest: Channel to operate on
> > > > > > + * @tid:   Channel configuration
> > > > > > + * @tuser: Tuser configuration
> > > > > > + * @ax_user: ax_user value
> > > > > > + * @ax_cache: ax_cache value
> > > > > > + */
> > > > > > +struct xilinx_mcdma_config {
> > > > > > +	u8 tdest;
> > > > > > +	u8 tid;
> > > > > > +	u8 tuser;
> > > > > > +	u8 ax_user;
> > > > > > +	u8 ax_cache;
> > > > >
> > > > > can you describe these in details, what do these do, what are
> > > > > the values to be programmed?
> > > >
> > > > As said above In Multi-Channel Mode each Stream interface can be
> > > > Configured up to 16 channels each channel is differentiated based
> > > > on the tdest
> > > and tid values.
> > >
> > > Then why are you not registering 16 channels for this? That should
> > > give you channel to operate on!
> >
> > The number of channels are configurable.
> > We are registering number of Channels that h/w configured for.
> >
> > Will fix in the next version. Will remove this config.
> > And based on the channel type will configure the h/w.
> 
> Looking at this you should redesign!
> 
> The vchan was designed to operate on 'virtual' channels. The hardware channels
> can be independent of that.
> 
> Your IP seems to be a good fit for that approach. Do not link the two and
> separate them. User can have a virtual channel. In your driver, you can manage
> hardware channels...

Fixed it in the v2 and posted the v2 series.
Please go thought it...

> 
> >
> > >
> > > >
> > > > tdest:
> > > > TDEST provides routing information for the data stream.
> > >
> > > pls elobrate
> >
> > Need to configure this with the channel number that We would like to
> > transfer data.
> 
> This should be internal to driver...

Fixed it in the v2 and posted the v2 series.
Please go thought it...

Regards,
Kedar.

> 
> --
> ~Vinod
Appana Durga Kedareswara rao June 28, 2016, 7:24 a.m. UTC | #7
Hi Vinod,

	
 
> > > > > > >  /**
> > > > > > > + * struct xilinx_mcdma_config - DMA Multi channel
> > > > > > > +configuration structure
> > > > > > > + * @tdest: Channel to operate on
> > > > > > > + * @tid:   Channel configuration
> > > > > > > + * @tuser: Tuser configuration
> > > > > > > + * @ax_user: ax_user value
> > > > > > > + * @ax_cache: ax_cache value  */ struct xilinx_mcdma_config
> > > > > > > +{
> > > > > > > +	u8 tdest;
> > > > > > > +	u8 tid;
> > > > > > > +	u8 tuser;
> > > > > > > +	u8 ax_user;
> > > > > > > +	u8 ax_cache;
> > > > > >
> > > > > > can you describe these in details, what do these do, what are
> > > > > > the values to be programmed?
> > > > >
> > > > > As said above In Multi-Channel Mode each Stream interface can be
> > > > > Configured up to 16 channels each channel is differentiated
> > > > > based on the tdest
> > > > and tid values.
> > > >
> > > > Then why are you not registering 16 channels for this? That should
> > > > give you channel to operate on!
> > >
> > > The number of channels are configurable.
> > > We are registering number of Channels that h/w configured for.
> > >
> > > Will fix in the next version. Will remove this config.
> > > And based on the channel type will configure the h/w.
> >
> > Looking at this you should redesign!
> >
> > The vchan was designed to operate on 'virtual' channels. The hardware
> > channels can be independent of that.
> >
> > Your IP seems to be a good fit for that approach. Do not link the two
> > and separate them. User can have a virtual channel. In your driver,
> > you can manage hardware channels...
> 
> Fixed it in the v2 and posted the v2 series.
> Please go thought it...

Sorry forgot to write explanation.
Without the need of virtual channels fixed it in the driver itself.
During probe based on the number of channels configuring the h/w
Accordingly user no need to aware of this configuration. User can request DMA
Channel in normal way the using dma_request_slave_channel or dma_request_channel
 Posted v2 series please go through it.

Regards,
Kedar.
diff mbox

Patch

diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c
index 20fe5ea..480b0ba 100644
--- a/drivers/dma/xilinx/xilinx_vdma.c
+++ b/drivers/dma/xilinx/xilinx_vdma.c
@@ -114,7 +114,7 @@ 
 #define XILINX_VDMA_REG_START_ADDRESS_64(n)	(0x000c + 8 * (n))
 
 /* HW specific definitions */
-#define XILINX_DMA_MAX_CHANS_PER_DEVICE	0x2
+#define XILINX_DMA_MAX_CHANS_PER_DEVICE	0x20
 
 #define XILINX_DMA_DMAXR_ALL_IRQ_MASK	\
 		(XILINX_DMA_DMASR_FRM_CNT_IRQ | \
@@ -165,6 +165,17 @@ 
 #define XILINX_DMA_COALESCE_MAX		255
 #define XILINX_DMA_NUM_APP_WORDS	5
 
+/* Multi-Channel DMA Descriptor offsets*/
+#define XILINX_DMA_MCRX_CDESC(x)	(0x40 + (x-1) * 0x20)
+#define XILINX_DMA_MCRX_TDESC(x)	(0x48 + (x-1) * 0x20)
+
+/* Multi-Channel DMA Masks/Shifts */
+#define XILINX_DMA_BD_HSIZE_MASK	GENMASK(15, 0)
+#define XILINX_DMA_BD_STRIDE_MASK	GENMASK(15, 0)
+#define XILINX_DMA_BD_VSIZE_MASK	GENMASK(31, 19)
+#define XILINX_DMA_BD_STRIDE_SHIFT	0
+#define XILINX_DMA_BD_VSIZE_SHIFT	19
+
 /* AXI CDMA Specific Registers/Offsets */
 #define XILINX_CDMA_REG_SRCADDR		0x18
 #define XILINX_CDMA_REG_DSTADDR		0x20
@@ -172,6 +183,13 @@ 
 /* AXI CDMA Specific Masks */
 #define XILINX_CDMA_CR_SGMODE          BIT(3)
 
+#define mm2s_mcdmatx_control(tdest, tid, tuser, axcache, aruser) \
+			     ((aruser << 28) | (axcache << 24) | \
+			     (tuser << 16) | (tid << 8) | (tdest))
+
+#define mm2s_mcdmarx_control(axcache, aruser) \
+			     ((aruser << 28) | (axcache << 24))
+
 /**
  * struct xilinx_vdma_desc_hw - Hardware Descriptor
  * @next_desc: Next Descriptor Pointer @0x00
@@ -210,8 +228,8 @@  struct xilinx_axidma_desc_hw {
 	u32 next_desc_msb;
 	u32 buf_addr;
 	u32 buf_addr_msb;
-	u32 pad1;
-	u32 pad2;
+	u32 mcdma_fields;
+	u32 vsize_stride;
 	u32 control;
 	u32 status;
 	u32 app[XILINX_DMA_NUM_APP_WORDS];
@@ -318,6 +336,7 @@  struct xilinx_dma_tx_descriptor {
  * @residue: Residue for AXI DMA
  * @seg_v: Statically allocated segments base
  * @cyclic_seg_v: Statically allocated segment base for cyclic transfers
+ * @mcdma_config: Device configuration info for MCDMA
  * @start_transfer: Differentiate b/w DMA IP's transfer
  */
 struct xilinx_dma_chan {
@@ -348,6 +367,7 @@  struct xilinx_dma_chan {
 	u32 residue;
 	struct xilinx_axidma_tx_segment *seg_v;
 	struct xilinx_axidma_tx_segment *cyclic_seg_v;
+	struct xilinx_mcdma_config mcdma_config;
 	void (*start_transfer)(struct xilinx_dma_chan *chan);
 };
 
@@ -365,6 +385,7 @@  struct xilinx_dma_config {
  * @common: DMA device structure
  * @chan: Driver specific DMA channel
  * @has_sg: Specifies whether Scatter-Gather is present or not
+ * @mcdma: Specifies whether Multi-Channel is present or not
  * @flush_on_fsync: Flush on frame sync
  * @ext_addr: Indicates 64 bit addressing is supported by dma device
  * @pdev: Platform device structure pointer
@@ -374,6 +395,8 @@  struct xilinx_dma_config {
  * @txs_clk: DMA mm2s stream clock
  * @rx_clk: DMA s2mm clock
  * @rxs_clk: DMA s2mm stream clock
+ * @nr_channels: Number of channels DMA device supports
+ * @chan_id: DMA channel identifier
  */
 struct xilinx_dma_device {
 	void __iomem *regs;
@@ -381,6 +404,7 @@  struct xilinx_dma_device {
 	struct dma_device common;
 	struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE];
 	bool has_sg;
+	bool mcdma;
 	u32 flush_on_fsync;
 	bool ext_addr;
 	struct platform_device  *pdev;
@@ -390,6 +414,8 @@  struct xilinx_dma_device {
 	struct clk *txs_clk;
 	struct clk *rx_clk;
 	struct clk *rxs_clk;
+	u32 nr_channels;
+	u32 chan_id;
 };
 
 /* Macros */
@@ -1174,6 +1200,7 @@  static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 {
 	struct xilinx_dma_tx_descriptor *head_desc, *tail_desc;
 	struct xilinx_axidma_tx_segment *tail_segment, *old_head, *new_head;
+	struct xilinx_mcdma_config *config = &chan->mcdma_config;
 	u32 reg;
 
 	if (chan->err)
@@ -1196,18 +1223,20 @@  static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 	tail_segment = list_last_entry(&tail_desc->segments,
 				       struct xilinx_axidma_tx_segment, node);
 
-	old_head = list_first_entry(&head_desc->segments,
-				struct xilinx_axidma_tx_segment, node);
-	new_head = chan->seg_v;
-	/* Copy Buffer Descriptor fields. */
-	new_head->hw = old_head->hw;
+	if (chan->has_sg && !chan->xdev->mcdma) {
+		old_head = list_first_entry(&head_desc->segments,
+					struct xilinx_axidma_tx_segment, node);
+		new_head = chan->seg_v;
+		/* Copy Buffer Descriptor fields. */
+		new_head->hw = old_head->hw;
 
-	/* Swap and save new reserve */
-	list_replace_init(&old_head->node, &new_head->node);
-	chan->seg_v = old_head;
+		/* Swap and save new reserve */
+		list_replace_init(&old_head->node, &new_head->node);
+		chan->seg_v = old_head;
 
-	tail_segment->hw.next_desc = chan->seg_v->phys;
-	head_desc->async_tx.phys = new_head->phys;
+		tail_segment->hw.next_desc = chan->seg_v->phys;
+		head_desc->async_tx.phys = new_head->phys;
+	}
 
 	reg = dma_ctrl_read(chan, XILINX_DMA_REG_DMACR);
 
@@ -1218,23 +1247,53 @@  static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
 		dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg);
 	}
 
-	if (chan->has_sg)
+	if (chan->has_sg && !chan->xdev->mcdma)
 		xilinx_write(chan, XILINX_DMA_REG_CURDESC,
 			     head_desc->async_tx.phys);
 
+	if (chan->has_sg && chan->xdev->mcdma) {
+		if (chan->direction == DMA_MEM_TO_DEV) {
+			dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC,
+				       head_desc->async_tx.phys);
+		} else {
+			if (!config->tdest) {
+				dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC,
+				       head_desc->async_tx.phys);
+			} else {
+				dma_ctrl_write(chan,
+					XILINX_DMA_MCRX_CDESC(config->tdest),
+				       head_desc->async_tx.phys);
+			}
+		}
+	}
+
 	xilinx_dma_start(chan);
 
 	if (chan->err)
 		return;
 
 	/* Start the transfer */
-	if (chan->has_sg) {
+	if (chan->has_sg && !chan->xdev->mcdma) {
 		if (chan->cyclic)
 			xilinx_write(chan, XILINX_DMA_REG_TAILDESC,
 				     chan->cyclic_seg_v->phys);
 		else
 			xilinx_write(chan, XILINX_DMA_REG_TAILDESC,
 				     tail_segment->phys);
+	} else if (chan->has_sg && chan->xdev->mcdma) {
+		if (chan->direction == DMA_MEM_TO_DEV) {
+			dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
+			       tail_segment->phys);
+		} else {
+			if (!config->tdest) {
+				dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC,
+					       tail_segment->phys);
+			} else {
+				dma_ctrl_write(chan,
+					XILINX_DMA_MCRX_TDESC(config->tdest),
+					tail_segment->phys);
+			}
+		}
 	} else {
 		struct xilinx_axidma_tx_segment *segment;
 		struct xilinx_axidma_desc_hw *hw;
@@ -1856,6 +1915,98 @@  error:
 }
 
 /**
+ * xilinx_dma_prep_interleaved - prepare a descriptor for a
+ *	DMA_SLAVE transaction
+ * @dchan: DMA channel
+ * @xt: Interleaved template pointer
+ * @flags: transfer ack flags
+ *
+ * Return: Async transaction descriptor on success and NULL on failure
+ */
+static struct dma_async_tx_descriptor *
+xilinx_dma_prep_interleaved(struct dma_chan *dchan,
+				 struct dma_interleaved_template *xt,
+				 unsigned long flags)
+{
+	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+	struct xilinx_mcdma_config *config = &chan->mcdma_config;
+	struct xilinx_dma_tx_descriptor *desc;
+	struct xilinx_axidma_tx_segment *segment;
+	struct xilinx_axidma_desc_hw *hw;
+
+	if (!is_slave_direction(xt->dir))
+		return NULL;
+
+	if (!xt->numf || !xt->sgl[0].size)
+		return NULL;
+
+	if (xt->frame_size != 1)
+		return NULL;
+
+	/* Allocate a transaction descriptor. */
+	desc = xilinx_dma_alloc_tx_descriptor(chan);
+	if (!desc)
+		return NULL;
+
+	chan->direction = xt->dir;
+	dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
+	desc->async_tx.tx_submit = xilinx_dma_tx_submit;
+
+	/* Get a free segment */
+	segment = xilinx_axidma_alloc_tx_segment(chan);
+	if (!segment)
+		goto error;
+
+	hw = &segment->hw;
+
+	/* Fill in the descriptor */
+	if (xt->dir != DMA_MEM_TO_DEV) {
+		hw->buf_addr = xt->dst_start;
+		hw->mcdma_fields = mm2s_mcdmarx_control(config->ax_cache,
+							config->ax_user);
+	} else {
+		hw->buf_addr = xt->src_start;
+		hw->mcdma_fields = mm2s_mcdmatx_control(config->tdest,
+							config->tid,
+							config->tuser,
+							config->ax_cache,
+							config->ax_user);
+	}
+
+	hw->vsize_stride = (xt->numf << XILINX_DMA_BD_VSIZE_SHIFT) &
+			    XILINX_DMA_BD_VSIZE_MASK;
+	hw->vsize_stride |= (xt->sgl[0].icg + xt->sgl[0].size) &
+			    XILINX_DMA_BD_STRIDE_MASK;
+	hw->control = xt->sgl[0].size & XILINX_DMA_BD_HSIZE_MASK;
+
+	/*
+	 * Insert the segment into the descriptor segments
+	 * list.
+	 */
+	list_add_tail(&segment->node, &desc->segments);
+
+
+	segment = list_first_entry(&desc->segments,
+				   struct xilinx_axidma_tx_segment, node);
+	desc->async_tx.phys = segment->phys;
+
+	/* For the last DMA_MEM_TO_DEV transfer, set EOP */
+	if (xt->dir == DMA_MEM_TO_DEV) {
+		segment->hw.control |= XILINX_DMA_BD_SOP;
+		segment = list_last_entry(&desc->segments,
+					  struct xilinx_axidma_tx_segment,
+					  node);
+		segment->hw.control |= XILINX_DMA_BD_EOP;
+	}
+
+	return &desc->async_tx;
+
+error:
+	xilinx_dma_free_tx_descriptor(chan, desc);
+	return NULL;
+}
+
+/**
  * xilinx_dma_terminate_all - Halt the channel and free descriptors
  * @chan: Driver specific DMA Channel pointer
  */
@@ -1948,6 +2099,21 @@  int xilinx_vdma_channel_set_config(struct dma_chan *dchan,
 }
 EXPORT_SYMBOL(xilinx_vdma_channel_set_config);
 
+int xilinx_dma_channel_mcdma_set_config(struct dma_chan *dchan,
+					struct xilinx_mcdma_config *cfg)
+{
+	struct xilinx_dma_chan *chan = to_xilinx_chan(dchan);
+
+	chan->mcdma_config.tdest = cfg->tdest;
+	chan->mcdma_config.tid = cfg->tid;
+	chan->mcdma_config.tuser = cfg->tuser;
+	chan->mcdma_config.ax_user = cfg->ax_user;
+	chan->mcdma_config.ax_cache = cfg->ax_cache;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xilinx_dma_channel_mcdma_set_config);
+
 /* -----------------------------------------------------------------------------
  * Probe and remove
  */
@@ -2170,7 +2336,7 @@  static void xdma_disable_allclks(struct xilinx_dma_device *xdev)
  * Return: '0' on success and failure value on error
  */
 static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
-				  struct device_node *node)
+				  struct device_node *node, int chan_id)
 {
 	struct xilinx_dma_chan *chan;
 	bool has_dre = false;
@@ -2214,7 +2380,7 @@  static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 
 	if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel")) {
 		chan->direction = DMA_MEM_TO_DEV;
-		chan->id = 0;
+		chan->id = chan_id;
 
 		chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET;
 		if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
@@ -2227,7 +2393,7 @@  static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 	} else if (of_device_is_compatible(node,
 					    "xlnx,axi-vdma-s2mm-channel")) {
 		chan->direction = DMA_DEV_TO_MEM;
-		chan->id = 1;
+		chan->id = chan_id;
 
 		chan->ctrl_offset = XILINX_DMA_S2MM_CTRL_OFFSET;
 		if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
@@ -2282,6 +2448,32 @@  static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 }
 
 /**
+ * xilinx_dma_child_probe - Per child node probe
+ * It get number of dma-channels per child node from
+ * device-tree and initializes all the channels.
+ *
+ * @xdev: Driver specific device structure
+ * @node: Device node
+ *
+ * Return: 0 always.
+ */
+static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
+				    struct device_node *node) {
+	int ret, i, nr_channels = 1;
+
+	ret = of_property_read_u32(node, "dma-channels", &nr_channels);
+	if ((ret < 0) && xdev->mcdma)
+		dev_warn(xdev->dev, "missing dma-channels property\n");
+
+	xdev->nr_channels += nr_channels;
+
+	for (i = 0; i < nr_channels; i++)
+		xilinx_dma_chan_probe(xdev, node, xdev->chan_id++);
+
+	return 0;
+}
+
+/**
  * of_dma_xilinx_xlate - Translation function
  * @dma_spec: Pointer to DMA specifier as found in the device tree
  * @ofdma: Pointer to DMA controller data
@@ -2294,7 +2486,7 @@  static struct dma_chan *of_dma_xilinx_xlate(struct of_phandle_args *dma_spec,
 	struct xilinx_dma_device *xdev = ofdma->of_dma_data;
 	int chan_id = dma_spec->args[0];
 
-	if (chan_id >= XILINX_DMA_MAX_CHANS_PER_DEVICE || !xdev->chan[chan_id])
+	if (chan_id >= xdev->nr_channels || !xdev->chan[chan_id])
 		return NULL;
 
 	return dma_get_slave_channel(&xdev->chan[chan_id]->common);
@@ -2370,6 +2562,8 @@  static int xilinx_dma_probe(struct platform_device *pdev)
 
 	/* Retrieve the DMA engine properties from the device tree */
 	xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
+	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA)
+		xdev->mcdma = of_property_read_bool(node, "xlnx,mcdma");
 
 	if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
 		err = of_property_read_u32(node, "xlnx,num-fstores",
@@ -2420,6 +2614,8 @@  static int xilinx_dma_probe(struct platform_device *pdev)
 		xdev->common.device_prep_slave_sg = xilinx_dma_prep_slave_sg;
 		xdev->common.device_prep_dma_cyclic =
 					  xilinx_dma_prep_dma_cyclic;
+		xdev->common.device_prep_interleaved_dma =
+					xilinx_dma_prep_interleaved;
 		/* Residue calculation is supported by only AXI DMA */
 		xdev->common.residue_granularity =
 					  DMA_RESIDUE_GRANULARITY_SEGMENT;
@@ -2435,13 +2631,13 @@  static int xilinx_dma_probe(struct platform_device *pdev)
 
 	/* Initialize the channels */
 	for_each_child_of_node(node, child) {
-		err = xilinx_dma_chan_probe(xdev, child);
+		err = xilinx_dma_child_probe(xdev, child);
 		if (err < 0)
 			goto disable_clks;
 	}
 
 	if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
-		for (i = 0; i < XILINX_DMA_MAX_CHANS_PER_DEVICE; i++)
+		for (i = 0; i < xdev->nr_channels; i++)
 			if (xdev->chan[i])
 				xdev->chan[i]->num_frms = num_frames;
 	}
@@ -2464,7 +2660,7 @@  static int xilinx_dma_probe(struct platform_device *pdev)
 disable_clks:
 	xdma_disable_allclks(xdev);
 error:
-	for (i = 0; i < XILINX_DMA_MAX_CHANS_PER_DEVICE; i++)
+	for (i = 0; i < xdev->nr_channels; i++)
 		if (xdev->chan[i])
 			xilinx_dma_chan_remove(xdev->chan[i]);
 
@@ -2486,7 +2682,7 @@  static int xilinx_dma_remove(struct platform_device *pdev)
 
 	dma_async_device_unregister(&xdev->common);
 
-	for (i = 0; i < XILINX_DMA_MAX_CHANS_PER_DEVICE; i++)
+	for (i = 0; i < xdev->nr_channels; i++)
 		if (xdev->chan[i])
 			xilinx_dma_chan_remove(xdev->chan[i]);
 
diff --git a/include/linux/dma/xilinx_dma.h b/include/linux/dma/xilinx_dma.h
index 7d7312f..dbb05df 100644
--- a/include/linux/dma/xilinx_dma.h
+++ b/include/linux/dma/xilinx_dma.h
@@ -60,6 +60,22 @@  struct zynqmp_dma_config {
 };
 
 /**
+ * struct xilinx_mcdma_config - DMA Multi channel configuration structure
+ * @tdest: Channel to operate on
+ * @tid:   Channel configuration
+ * @tuser: Tuser configuration
+ * @ax_user: ax_user value
+ * @ax_cache: ax_cache value
+ */
+struct xilinx_mcdma_config {
+	u8 tdest;
+	u8 tid;
+	u8 tuser;
+	u8 ax_user;
+	u8 ax_cache;
+};
+
+/**
  * enum xdma_ip_type: DMA IP type.
  *
  * XDMA_TYPE_AXIDMA: Axi dma ip.
@@ -77,4 +93,6 @@  int xilinx_vdma_channel_set_config(struct dma_chan *dchan,
 					struct xilinx_vdma_config *cfg);
 int zynqmp_dma_channel_set_config(struct dma_chan *dchan,
 					struct zynqmp_dma_config *cfg);
+int xilinx_dma_channel_mcdma_set_config(struct dma_chan *dchan,
+					struct xilinx_mcdma_config *cfg);
 #endif