diff mbox series

[10/19] dma: imx-sdma: Add multi fifo support

Message ID 20220317082818.503143-11-s.hauer@pengutronix.de (mailing list archive)
State Superseded
Headers show
Series ASoC: fsl_micfil: Driver updates | expand

Commit Message

Sascha Hauer March 17, 2022, 8:28 a.m. UTC
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/dma/imx-sdma.c                | 54 +++++++++++++++++++++++++++
 include/linux/platform_data/dma-imx.h |  7 ++++
 2 files changed, 61 insertions(+)

Comments

Sascha Hauer March 17, 2022, 10:19 a.m. UTC | #1
On Thu, Mar 17, 2022 at 05:08:55PM +0800, Shengjiu Wang wrote:
>    On Thu, Mar 17, 2022 at 4:28 PM Sascha Hauer <[1]s.hauer@pengutronix.de>
>    wrote:
> 
>      +struct sdma_peripheral_config {
>      +       int n_fifos_src;
>      +       int n_fifos_dst;
>      +       bool sw_done;
>      +};
>      +
>       #endif
> 
>    Hi Sascha
>    This is our internal definition for this sdma_peripheral_config.
>    Could you please adopt this?

This structure is completely internal to the kernel and can be adjusted
when we need it. I don't see a reason to add unused fields to it just to
be compatible with a downstream kernel.

Sascha

>    /**
>     * struct sdma_audio_config - special sdma config for audio case
>     * @src_fifo_num: source fifo number for mcu_2_sai/sai_2_mcu script
>     *                For example, if there are 4 fifos, sdma will fetch
>     *                fifos one by one and roll back to the first fifo after
>     *                the 4th fifo fetch.
>     * @dst_fifo_num: similar as src_fifo_num, but dest fifo instead.
>     * @src_fifo_off: source fifo offset, 0 means all fifos are continuous, 1
>     *                means 1 word offset between fifos. All offset between
>     *                fifos should be same.
>     * @dst_fifo_off: dst fifo offset, similar as @src_fifo_off.
>     * @words_per_fifo: numbers of words per fifo fetch/fill, 0 means
>     *                  one channel per fifo, 1 means 2 channels per fifo..
>     *                  If 'src_fifo_num =  4' and 'chans_per_fifo = 1', it
>     *                  means the first two words(channels) fetch from fifo1
>     *                  and then jump to fifo2 for next two words, and so on
>     *                  after the last fifo4 fetched, roll back to fifo1.
>     * @sw_done_sel: software done selector, PDM need enable software done
>    feature
>     *               in mcu_2_sai/sai_2_mcu script.
>     *               Bit31: sw_done eanbled or not
>     *               Bit16~Bit0: selector
>     *               For example: 0x80000000 means sw_done enabled for done0
>     *                            sector which is for PDM on i.mx8mm.
>     */
>    struct sdma_audio_config {
>            u8 src_fifo_num;
>            u8 dst_fifo_num;
>            u8 src_fifo_off;
>            u8 dst_fifo_off;
>            u8 words_per_fifo;
>            u32 sw_done_sel;
>    };
>    best regards
>    wang shengjiu
>     
> 
> References
> 
>    Visible links
>    1. mailto:s.hauer@pengutronix.de
>    2. mailto:s.hauer@pengutronix.de
Sascha Hauer March 17, 2022, 2:41 p.m. UTC | #2
On Thu, Mar 17, 2022 at 08:20:22PM +0800, Shengjiu Wang wrote:
>    Hi
>    On Thu, Mar 17, 2022 at 6:19 PM Sascha Hauer <[1]s.hauer@pengutronix.de>
>    wrote:
> 
>      On Thu, Mar 17, 2022 at 05:08:55PM +0800, Shengjiu Wang wrote:
>      >    On Thu, Mar 17, 2022 at 4:28 PM Sascha Hauer
>      <[1][2]s.hauer@pengutronix.de>
>      >    wrote:
>      >
>      >      +struct sdma_peripheral_config {
>      >      +       int n_fifos_src;
>      >      +       int n_fifos_dst;
>      >      +       bool sw_done;
>      >      +};
>      >      +
>      >       #endif
>      >
>      >    Hi Sascha
>      >    This is our internal definition for this sdma_peripheral_config.
>      >    Could you please adopt this?
> 
>      This structure is completely internal to the kernel and can be adjusted
>      when we need it. I don't see a reason to add unused fields to it just to
>      be compatible with a downstream kernel.
> 
>    Yes, it is not used by micfil. But the fifo_offset and words_per_fifo
>    is part the multi fifo script support scope, if only add fifo_num,  it
>    looks
>    like this feature is not complete.

No, it's not. I only added the parts that I am interested in and that I
can test. I have some multichannel audio stuff in my pipeline, I might add
more pieces later.

>    By the way,  which multi fifo script version are you using? seems it is
>    not the latest compared with our release, right?

I am using the latest firmware from linux-firmware.git.

Sascha
Sascha Hauer March 18, 2022, 8:04 a.m. UTC | #3
On Fri, Mar 18, 2022 at 01:42:51PM +0800, Shengjiu Wang wrote:
>    Hi
>    On Thu, Mar 17, 2022 at 4:28 PM Sascha Hauer <[1]s.hauer@pengutronix.de>
>    wrote:
> 
>      Signed-off-by: Sascha Hauer <[2]s.hauer@pengutronix.de>
>      ---
>       drivers/dma/imx-sdma.c                | 54 +++++++++++++++++++++++++++
>       include/linux/platform_data/dma-imx.h |  7 ++++
>       2 files changed, 61 insertions(+)
> 
>      diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
>      index 1038f6bc7f846..21e1cec2ffde9 100644
>      --- a/drivers/dma/imx-sdma.c
>      +++ b/drivers/dma/imx-sdma.c
>      @@ -14,6 +14,7 @@
>       #include <linux/iopoll.h>
>       #include <linux/module.h>
>       #include <linux/types.h>
>      +#include <linux/bitfield.h>
>       #include <linux/bitops.h>
>       #include <linux/mm.h>
>       #include <linux/interrupt.h>
>      @@ -73,6 +74,7 @@
>       #define SDMA_CHNENBL0_IMX35    0x200
>       #define SDMA_CHNENBL0_IMX31    0x080
>       #define SDMA_CHNPRI_0          0x100
>      +#define SDMA_DONE0_CONFIG      0x1000
> 
>       /*
>        * Buffer descriptor status values.
>      @@ -180,6 +182,12 @@
>                                       BIT(DMA_MEM_TO_DEV) | \
>                                       BIT(DMA_DEV_TO_DEV))
> 
>      +#define SDMA_WATERMARK_LEVEL_N_FIFOS   GENMASK(15, 12)
>      +#define SDMA_WATERMARK_LEVEL_SW_DONE   BIT(23)
>      +
>      +#define SDMA_DONE0_CONFIG_DONE_SEL     BIT(7)
>      +#define SDMA_DONE0_CONFIG_DONE_DIS     BIT(6)
>      +
>       /**
>        * struct sdma_script_start_addrs - SDMA script start pointers
>        *
>      @@ -441,6 +449,11 @@ struct sdma_channel {
>              struct work_struct              terminate_worker;
>              struct list_head                terminated;
>              bool                            is_ram_script;
>      +       unsigned int                    n_fifos;
>      +       unsigned int                    n_fifos_src;
>      +       unsigned int                    n_fifos_dst;
>      +       bool                            sw_done;
>      +       u32                             sw_done_sel;
> 
>    "sw_done_sel" is not used, and may not be needed.

Ok, will drop.

>    And can we just add 'struct sdma_peripheral_config *pconfig'
>    to replace each item here ('n_fifos_src', 'n_fifos_dst',
>    'sw_done')? 

I rather do not access the pointer to the peripheral_config outside of
sdma_config because I know nothing about the lifetime of that structure.

>    the pconfig can point to the struct in dma_slave_config.
>    And 'n_fifos' can be moved to locally function in 
>    sdma_set_watermarklevel_for_sais(), then use sdmac->direction
>    to select 'n_fifos_dst' or 'n_fifos_src'.

Ok.

Sascha
diff mbox series

Patch

diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 1038f6bc7f846..21e1cec2ffde9 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -14,6 +14,7 @@ 
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
@@ -73,6 +74,7 @@ 
 #define SDMA_CHNENBL0_IMX35	0x200
 #define SDMA_CHNENBL0_IMX31	0x080
 #define SDMA_CHNPRI_0		0x100
+#define SDMA_DONE0_CONFIG	0x1000
 
 /*
  * Buffer descriptor status values.
@@ -180,6 +182,12 @@ 
 				 BIT(DMA_MEM_TO_DEV) | \
 				 BIT(DMA_DEV_TO_DEV))
 
+#define SDMA_WATERMARK_LEVEL_N_FIFOS	GENMASK(15, 12)
+#define SDMA_WATERMARK_LEVEL_SW_DONE	BIT(23)
+
+#define SDMA_DONE0_CONFIG_DONE_SEL	BIT(7)
+#define SDMA_DONE0_CONFIG_DONE_DIS	BIT(6)
+
 /**
  * struct sdma_script_start_addrs - SDMA script start pointers
  *
@@ -441,6 +449,11 @@  struct sdma_channel {
 	struct work_struct		terminate_worker;
 	struct list_head                terminated;
 	bool				is_ram_script;
+	unsigned int			n_fifos;
+	unsigned int			n_fifos_src;
+	unsigned int			n_fifos_dst;
+	bool				sw_done;
+	u32				sw_done_sel;
 };
 
 #define IMX_DMA_SG_LOOP		BIT(0)
@@ -773,6 +786,14 @@  static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event)
 	val = readl_relaxed(sdma->regs + chnenbl);
 	__set_bit(channel, &val);
 	writel_relaxed(val, sdma->regs + chnenbl);
+
+	/* Set SDMA_DONEx_CONFIG is sw_done enabled */
+	if (sdmac->sw_done) {
+		val = readl_relaxed(sdma->regs + SDMA_DONE0_CONFIG);
+		val |= SDMA_DONE0_CONFIG_DONE_SEL;
+		val &= ~SDMA_DONE0_CONFIG_DONE_DIS;
+		writel_relaxed(val, sdma->regs + SDMA_DONE0_CONFIG);
+	}
 }
 
 static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
@@ -1022,6 +1043,10 @@  static int sdma_get_pc(struct sdma_channel *sdmac,
 	case IMX_DMATYPE_IPU_MEMORY:
 		emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr;
 		break;
+	case IMX_DMATYPE_MULTI_SAI:
+		per_2_emi = sdma->script_addrs->sai_2_mcu_addr;
+		emi_2_per = sdma->script_addrs->mcu_2_sai_addr;
+		break;
 	default:
 		dev_err(sdma->dev, "Unsupported transfer type %d\n",
 			peripheral_type);
@@ -1198,6 +1223,15 @@  static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
 	sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT;
 }
 
+static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac)
+{
+	if (sdmac->sw_done)
+		sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE;
+
+	sdmac->watermark_level |=
+			FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, sdmac->n_fifos);
+}
+
 static int sdma_config_channel(struct dma_chan *chan)
 {
 	struct sdma_channel *sdmac = to_sdma_chan(chan);
@@ -1234,6 +1268,10 @@  static int sdma_config_channel(struct dma_chan *chan)
 			    sdmac->peripheral_type == IMX_DMATYPE_ASRC)
 				sdma_set_watermarklevel_for_p2p(sdmac);
 		} else {
+			if (sdmac->peripheral_type ==
+					IMX_DMATYPE_MULTI_SAI)
+				sdma_set_watermarklevel_for_sais(sdmac);
+
 			__set_bit(sdmac->event_id0, sdmac->event_mask);
 		}
 
@@ -1669,6 +1707,7 @@  static int sdma_config_write(struct dma_chan *chan,
 		sdmac->watermark_level = dmaengine_cfg->src_maxburst *
 			dmaengine_cfg->src_addr_width;
 		sdmac->word_size = dmaengine_cfg->src_addr_width;
+		sdmac->n_fifos =  sdmac->n_fifos_src;
 	} else if (direction == DMA_DEV_TO_DEV) {
 		sdmac->per_address2 = dmaengine_cfg->src_addr;
 		sdmac->per_address = dmaengine_cfg->dst_addr;
@@ -1682,6 +1721,7 @@  static int sdma_config_write(struct dma_chan *chan,
 		sdmac->watermark_level = dmaengine_cfg->dst_maxburst *
 			dmaengine_cfg->dst_addr_width;
 		sdmac->word_size = dmaengine_cfg->dst_addr_width;
+		sdmac->n_fifos =  sdmac->n_fifos_dst;
 	}
 	sdmac->direction = direction;
 	return sdma_config_channel(chan);
@@ -1691,9 +1731,23 @@  static int sdma_config(struct dma_chan *chan,
 		       struct dma_slave_config *dmaengine_cfg)
 {
 	struct sdma_channel *sdmac = to_sdma_chan(chan);
+	struct sdma_engine *sdma = sdmac->sdma;
 
 	memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg));
 
+	if (dmaengine_cfg->peripheral_config) {
+		struct sdma_peripheral_config *sdmacfg = dmaengine_cfg->peripheral_config;
+		if (dmaengine_cfg->peripheral_size != sizeof(struct sdma_peripheral_config)) {
+			dev_err(sdma->dev, "Invalid peripheral size %zu, expected %zu\n",
+				dmaengine_cfg->peripheral_size,
+				sizeof(struct sdma_peripheral_config));
+			return -EINVAL;
+		}
+		sdmac->n_fifos_src = sdmacfg->n_fifos_src;
+		sdmac->n_fifos_dst = sdmacfg->n_fifos_dst;
+		sdmac->sw_done = sdmacfg->sw_done;
+	}
+
 	/* Set ENBLn earlier to make sure dma request triggered after that */
 	if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events)
 		return -EINVAL;
diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h
index 281adbb26e6bd..4a43a048e1b4d 100644
--- a/include/linux/platform_data/dma-imx.h
+++ b/include/linux/platform_data/dma-imx.h
@@ -39,6 +39,7 @@  enum sdma_peripheral_type {
 	IMX_DMATYPE_SSI_DUAL,	/* SSI Dual FIFO */
 	IMX_DMATYPE_ASRC_SP,	/* Shared ASRC */
 	IMX_DMATYPE_SAI,	/* SAI */
+	IMX_DMATYPE_MULTI_SAI,	/* MULTI FIFOs For Audio */
 };
 
 enum imx_dma_prio {
@@ -65,4 +66,10 @@  static inline int imx_dma_is_general_purpose(struct dma_chan *chan)
 		!strcmp(chan->device->dev->driver->name, "imx-dma");
 }
 
+struct sdma_peripheral_config {
+	int n_fifos_src;
+	int n_fifos_dst;
+	bool sw_done;
+};
+
 #endif