Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: imx-sdma: Add multi fifo support

The i.MX SDMA engine can read from / write to multiple successive
hardware FIFO registers, referred to as "Multi FIFO support". This is
needed for the micfil driver and certain configurations of the SAI
driver. This patch adds support for this feature.

The number of FIFOs to read from / write to must be communicated from
the client driver to the SDMA engine. For this the struct
dma_slave_config::peripheral_config field is used.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-By: Vinod Koul <vkoul@kernel.org>
Link: https://lore.kernel.org/r/20220414162249.3934543-12-s.hauer@pengutronix.de
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Sascha Hauer and committed by
Mark Brown
824a0a02 625d8936

+77
+57
drivers/dma/imx-sdma.c
··· 14 14 #include <linux/iopoll.h> 15 15 #include <linux/module.h> 16 16 #include <linux/types.h> 17 + #include <linux/bitfield.h> 17 18 #include <linux/bitops.h> 18 19 #include <linux/mm.h> 19 20 #include <linux/interrupt.h> ··· 74 73 #define SDMA_CHNENBL0_IMX35 0x200 75 74 #define SDMA_CHNENBL0_IMX31 0x080 76 75 #define SDMA_CHNPRI_0 0x100 76 + #define SDMA_DONE0_CONFIG 0x1000 77 77 78 78 /* 79 79 * Buffer descriptor status values. ··· 181 179 #define SDMA_DMA_DIRECTIONS (BIT(DMA_DEV_TO_MEM) | \ 182 180 BIT(DMA_MEM_TO_DEV) | \ 183 181 BIT(DMA_DEV_TO_DEV)) 182 + 183 + #define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12) 184 + #define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23) 185 + 186 + #define SDMA_DONE0_CONFIG_DONE_SEL BIT(7) 187 + #define SDMA_DONE0_CONFIG_DONE_DIS BIT(6) 184 188 185 189 /** 186 190 * struct sdma_script_start_addrs - SDMA script start pointers ··· 449 441 struct work_struct terminate_worker; 450 442 struct list_head terminated; 451 443 bool is_ram_script; 444 + unsigned int n_fifos_src; 445 + unsigned int n_fifos_dst; 446 + bool sw_done; 452 447 }; 453 448 454 449 #define IMX_DMA_SG_LOOP BIT(0) ··· 789 778 val = readl_relaxed(sdma->regs + chnenbl); 790 779 __set_bit(channel, &val); 791 780 writel_relaxed(val, sdma->regs + chnenbl); 781 + 782 + /* Set SDMA_DONEx_CONFIG is sw_done enabled */ 783 + if (sdmac->sw_done) { 784 + val = readl_relaxed(sdma->regs + SDMA_DONE0_CONFIG); 785 + val |= SDMA_DONE0_CONFIG_DONE_SEL; 786 + val &= ~SDMA_DONE0_CONFIG_DONE_DIS; 787 + writel_relaxed(val, sdma->regs + SDMA_DONE0_CONFIG); 788 + } 792 789 } 793 790 794 791 static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) ··· 1057 1038 case IMX_DMATYPE_IPU_MEMORY: 1058 1039 emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; 1059 1040 break; 1041 + case IMX_DMATYPE_MULTI_SAI: 1042 + per_2_emi = sdma->script_addrs->sai_2_mcu_addr; 1043 + emi_2_per = sdma->script_addrs->mcu_2_sai_addr; 1044 + break; 1060 1045 default: 1061 1046 dev_err(sdma->dev, "Unsupported transfer type %d\n", 1062 1047 peripheral_type); ··· 1237 1214 sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT; 1238 1215 } 1239 1216 1217 + static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac) 1218 + { 1219 + unsigned int n_fifos; 1220 + 1221 + if (sdmac->sw_done) 1222 + sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE; 1223 + 1224 + if (sdmac->direction == DMA_DEV_TO_MEM) 1225 + n_fifos = sdmac->n_fifos_src; 1226 + else 1227 + n_fifos = sdmac->n_fifos_dst; 1228 + 1229 + sdmac->watermark_level |= 1230 + FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos); 1231 + } 1232 + 1240 1233 static int sdma_config_channel(struct dma_chan *chan) 1241 1234 { 1242 1235 struct sdma_channel *sdmac = to_sdma_chan(chan); ··· 1289 1250 sdmac->peripheral_type == IMX_DMATYPE_ASRC) 1290 1251 sdma_set_watermarklevel_for_p2p(sdmac); 1291 1252 } else { 1253 + if (sdmac->peripheral_type == 1254 + IMX_DMATYPE_MULTI_SAI) 1255 + sdma_set_watermarklevel_for_sais(sdmac); 1256 + 1292 1257 __set_bit(sdmac->event_id0, sdmac->event_mask); 1293 1258 } 1294 1259 ··· 1750 1707 struct dma_slave_config *dmaengine_cfg) 1751 1708 { 1752 1709 struct sdma_channel *sdmac = to_sdma_chan(chan); 1710 + struct sdma_engine *sdma = sdmac->sdma; 1753 1711 1754 1712 memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg)); 1713 + 1714 + if (dmaengine_cfg->peripheral_config) { 1715 + struct sdma_peripheral_config *sdmacfg = dmaengine_cfg->peripheral_config; 1716 + if (dmaengine_cfg->peripheral_size != sizeof(struct sdma_peripheral_config)) { 1717 + dev_err(sdma->dev, "Invalid peripheral size %zu, expected %zu\n", 1718 + dmaengine_cfg->peripheral_size, 1719 + sizeof(struct sdma_peripheral_config)); 1720 + return -EINVAL; 1721 + } 1722 + sdmac->n_fifos_src = sdmacfg->n_fifos_src; 1723 + sdmac->n_fifos_dst = sdmacfg->n_fifos_dst; 1724 + sdmac->sw_done = sdmacfg->sw_done; 1725 + } 1755 1726 1756 1727 /* Set ENBLn earlier to make sure dma request triggered after that */ 1757 1728 if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events)
+20
include/linux/dma/imx-dma.h
··· 39 39 IMX_DMATYPE_SSI_DUAL, /* SSI Dual FIFO */ 40 40 IMX_DMATYPE_ASRC_SP, /* Shared ASRC */ 41 41 IMX_DMATYPE_SAI, /* SAI */ 42 + IMX_DMATYPE_MULTI_SAI, /* MULTI FIFOs For Audio */ 42 43 }; 43 44 44 45 enum imx_dma_prio { ··· 65 64 return !strcmp(chan->device->dev->driver->name, "imx-sdma") || 66 65 !strcmp(chan->device->dev->driver->name, "imx-dma"); 67 66 } 67 + 68 + /** 69 + * struct sdma_peripheral_config - SDMA config for audio 70 + * @n_fifos_src: Number of FIFOs for recording 71 + * @n_fifos_dst: Number of FIFOs for playback 72 + * @sw_done: Use software done. Needed for PDM (micfil) 73 + * 74 + * Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO 75 + * registers. For multichannel recording/playback the SAI/micfil have 76 + * one FIFO register per channel and the SDMA engine has to read/write 77 + * the next channel from/to the next register and wrap around to the 78 + * first register when all channels are handled. The number of active 79 + * channels must be communicated to the SDMA engine using this struct. 80 + */ 81 + struct sdma_peripheral_config { 82 + int n_fifos_src; 83 + int n_fifos_dst; 84 + bool sw_done; 85 + }; 68 86 69 87 #endif /* __LINUX_DMA_IMX_H */