Commit a46a7634 authored by Jarkko Nikula's avatar Jarkko Nikula Committed by Vinod Koul

dmaengine: dw: Fix data corruption in large device to memory transfers

When transferring more data than the maximum block size supported by the
HW multiplied by source width the transfer is split into smaller chunks.
Currently code calculates the memory width and thus aligment before
splitting for both memory to device and device to memory transfers.

For memory to device transfers this work fine since alignment is preserved
through the splitting and split blocks are still memory width aligned.
However in device to memory transfers aligment breaks when maximum block
size multiplied by register width doesn't have the same alignment than the
buffer. For instance when transferring from an 8-bit register 4100 bytes
(32-bit aligned) on a DW DMA that has maximum block size of 4095 elements.
An attempt to do such transfers caused data corruption.

Fix this by calculating and setting the destination memory width after
splitting by using the split block aligment and length.
Signed-off-by: default avatarJarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent ebb7fe21
...@@ -789,17 +789,13 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -789,17 +789,13 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
lli_write(desc, sar, mem); lli_write(desc, sar, mem);
lli_write(desc, dar, reg); lli_write(desc, dar, reg);
lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width));
if ((len >> mem_width) > dwc->block_size) { if ((len >> mem_width) > dwc->block_size) {
dlen = dwc->block_size << mem_width; dlen = dwc->block_size << mem_width;
mem += dlen;
len -= dlen;
} else { } else {
dlen = len; dlen = len;
len = 0;
} }
lli_write(desc, ctlhi, dlen >> mem_width); lli_write(desc, ctlhi, dlen >> mem_width);
lli_write(desc, ctllo, ctllo | DWC_CTLL_SRC_WIDTH(mem_width));
desc->len = dlen; desc->len = dlen;
if (!first) { if (!first) {
...@@ -809,6 +805,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -809,6 +805,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
list_add_tail(&desc->desc_node, &first->tx_list); list_add_tail(&desc->desc_node, &first->tx_list);
} }
prev = desc; prev = desc;
mem += dlen;
len -= dlen;
total_len += dlen; total_len += dlen;
if (len) if (len)
...@@ -833,8 +832,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -833,8 +832,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg); mem = sg_dma_address(sg);
len = sg_dma_len(sg); len = sg_dma_len(sg);
mem_width = __ffs(data_width | mem | len);
slave_sg_fromdev_fill_desc: slave_sg_fromdev_fill_desc:
desc = dwc_desc_get(dwc); desc = dwc_desc_get(dwc);
if (!desc) if (!desc)
...@@ -842,16 +839,14 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -842,16 +839,14 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
lli_write(desc, sar, reg); lli_write(desc, sar, reg);
lli_write(desc, dar, mem); lli_write(desc, dar, mem);
lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width));
if ((len >> reg_width) > dwc->block_size) { if ((len >> reg_width) > dwc->block_size) {
dlen = dwc->block_size << reg_width; dlen = dwc->block_size << reg_width;
mem += dlen;
len -= dlen;
} else { } else {
dlen = len; dlen = len;
len = 0;
} }
lli_write(desc, ctlhi, dlen >> reg_width); lli_write(desc, ctlhi, dlen >> reg_width);
mem_width = __ffs(data_width | mem | dlen);
lli_write(desc, ctllo, ctllo | DWC_CTLL_DST_WIDTH(mem_width));
desc->len = dlen; desc->len = dlen;
if (!first) { if (!first) {
...@@ -861,6 +856,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -861,6 +856,9 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
list_add_tail(&desc->desc_node, &first->tx_list); list_add_tail(&desc->desc_node, &first->tx_list);
} }
prev = desc; prev = desc;
mem += dlen;
len -= dlen;
total_len += dlen; total_len += dlen;
if (len) if (len)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment