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

Configure Feed

Select the types of activity you want to include in your feed.

dmaengine: sun4i: Set the maximum segment size

The sun4i DMA engine supports transfer sizes up to 128k for normal DMA
and 16M for dedicated DMA, as documented in the A10 and A20 manuals.

Since this is larger than the default segment size limit (64k), exposing
the real limit reduces the number of transfers needed for a transaction.
However, because the device can only report one segment size limit, we
have to expose the smaller limit from normal DMA.

One complication is that the driver combines pairs of periodic transfers
to reduce programming overhead. This only works when the period size is
at most half of the maximum transfer size. With the default 64k segment
size limit, this was always the case, but for normal DMA it is no longer
guaranteed. Skip the optimization if the period is too long; even
without it, the overhead is less than before.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Link: https://lore.kernel.org/r/20220621031350.36187-1-samuel@sholland.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Samuel Holland and committed by
Vinod Koul
a94a098a 9bef4929

+27 -5
+27 -5
drivers/dma/sun4i-dma.c
··· 7 7 #include <linux/bitmap.h> 8 8 #include <linux/bitops.h> 9 9 #include <linux/clk.h> 10 + #include <linux/dma-mapping.h> 10 11 #include <linux/dmaengine.h> 11 12 #include <linux/dmapool.h> 12 13 #include <linux/interrupt.h> ··· 123 122 SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) | \ 124 123 SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2)) 125 124 125 + /* 126 + * Normal DMA supports individual transfers (segments) up to 128k. 127 + * Dedicated DMA supports transfers up to 16M. We can only report 128 + * one size limit, so we have to use the smaller value. 129 + */ 130 + #define SUN4I_NDMA_MAX_SEG_SIZE SZ_128K 131 + #define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M 132 + #define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE 133 + 126 134 struct sun4i_dma_pchan { 127 135 /* Register base of channel */ 128 136 void __iomem *base; ··· 165 155 struct virt_dma_desc vd; 166 156 struct list_head demands; 167 157 struct list_head completed_demands; 168 - int is_cyclic; 158 + bool is_cyclic : 1; 159 + bool use_half_int : 1; 169 160 }; 170 161 171 162 struct sun4i_dma_dev { ··· 383 372 if (promise) { 384 373 vchan->contract = contract; 385 374 vchan->pchan = pchan; 386 - set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1); 375 + set_pchan_interrupt(priv, pchan, contract->use_half_int, 1); 387 376 configure_pchan(pchan, promise); 388 377 } 389 378 ··· 746 735 * 747 736 * Which requires half the engine programming for the same 748 737 * functionality. 738 + * 739 + * This only works if two periods fit in a single promise. That will 740 + * always be the case for dedicated DMA, where the hardware has a much 741 + * larger maximum transfer size than advertised to clients. 749 742 */ 750 - nr_periods = DIV_ROUND_UP(len / period_len, 2); 743 + if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) { 744 + period_len *= 2; 745 + contract->use_half_int = 1; 746 + } 747 + 748 + nr_periods = DIV_ROUND_UP(len, period_len); 751 749 for (i = 0; i < nr_periods; i++) { 752 750 /* Calculate the offset in the buffer and the length needed */ 753 - offset = i * period_len * 2; 754 - plength = min((len - offset), (period_len * 2)); 751 + offset = i * period_len; 752 + plength = min((len - offset), period_len); 755 753 if (dir == DMA_MEM_TO_DEV) 756 754 src = buf + offset; 757 755 else ··· 1168 1148 1169 1149 platform_set_drvdata(pdev, priv); 1170 1150 spin_lock_init(&priv->lock); 1151 + 1152 + dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE); 1171 1153 1172 1154 dma_cap_zero(priv->slave.cap_mask); 1173 1155 dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);