diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index f52d9fd18e57..c028011cc61c 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -40,38 +40,15 @@ u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr) return cpu_addr; } -static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk) -{ - struct dw_edma_burst *burst; - - burst = kzalloc_obj(*burst, GFP_NOWAIT); - if (unlikely(!burst)) - return NULL; - - INIT_LIST_HEAD(&burst->list); - if (chunk->burst) { - /* Create and add new element into the linked list */ - chunk->bursts_alloc++; - list_add_tail(&burst->list, &chunk->burst->list); - } else { - /* List head */ - chunk->bursts_alloc = 0; - chunk->burst = burst; - } - - return burst; -} - -static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) +static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc, u32 nburst) { struct dw_edma_chan *chan = desc->chan; struct dw_edma_chunk *chunk; - chunk = kzalloc_obj(*chunk, GFP_NOWAIT); + chunk = kzalloc_flex(*chunk, burst, nburst, GFP_NOWAIT); if (unlikely(!chunk)) return NULL; - INIT_LIST_HEAD(&chunk->list); chunk->chan = chan; /* Toggling change bit (CB) in each chunk, this is a mechanism to * inform the eDMA HW block that this is a new linked list ready @@ -81,20 +58,10 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc) */ chunk->cb = !(desc->chunks_alloc % 2); - if (desc->chunk) { - /* Create and add new element into the linked list */ - if (!dw_edma_alloc_burst(chunk)) { - kfree(chunk); - return NULL; - } - desc->chunks_alloc++; - list_add_tail(&chunk->list, &desc->chunk->list); - } else { - /* List head */ - chunk->burst = NULL; - desc->chunks_alloc = 0; - desc->chunk = chunk; - } + chunk->nburst = nburst; + + list_add_tail(&chunk->list, &desc->chunk_list); + desc->chunks_alloc++; return chunk; } @@ -108,53 +75,23 @@ static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan) return NULL; desc->chan = chan; - if (!dw_edma_alloc_chunk(desc)) { - kfree(desc); - return NULL; - } + + INIT_LIST_HEAD(&desc->chunk_list); return desc; } -static void dw_edma_free_burst(struct dw_edma_chunk *chunk) -{ - struct dw_edma_burst *child, *_next; - - /* Remove all the list elements */ - list_for_each_entry_safe(child, _next, &chunk->burst->list, list) { - list_del(&child->list); - kfree(child); - chunk->bursts_alloc--; - } - - /* Remove the list head */ - kfree(child); - chunk->burst = NULL; -} - -static void dw_edma_free_chunk(struct dw_edma_desc *desc) +static void dw_edma_free_desc(struct dw_edma_desc *desc) { struct dw_edma_chunk *child, *_next; - if (!desc->chunk) - return; - /* Remove all the list elements */ - list_for_each_entry_safe(child, _next, &desc->chunk->list, list) { - dw_edma_free_burst(child); + list_for_each_entry_safe(child, _next, &desc->chunk_list, list) { list_del(&child->list); kfree(child); desc->chunks_alloc--; } - /* Remove the list head */ - kfree(child); - desc->chunk = NULL; -} - -static void dw_edma_free_desc(struct dw_edma_desc *desc) -{ - dw_edma_free_chunk(desc); kfree(desc); } @@ -166,23 +103,17 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; - struct dw_edma_burst *child; u32 i = 0; - int j; if (chan->non_ll) { - child = list_first_entry_or_null(&chunk->burst->list, - struct dw_edma_burst, list); - if (child) - chan->dw->core->non_ll_start(chunk->chan, child); + if (chunk->nburst == 1) + chan->dw->core->non_ll_start(chunk->chan, &chunk->burst[0]); return; } - j = chunk->bursts_alloc; - list_for_each_entry(child, &chunk->burst->list, list) { - j--; - dw_edma_core_ll_data(chan, child, i++, chunk->cb, !j); - } + for (i = 0; i < chunk->nburst; i++) + dw_edma_core_ll_data(chan, &chunk->burst[i], i, chunk->cb, + i == chunk->nburst - 1); dw_edma_core_ll_link(chan, i, chunk->cb, chan->ll_region.paddr); @@ -206,14 +137,13 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan) if (!desc) return 0; - child = list_first_entry_or_null(&desc->chunk->list, + child = list_first_entry_or_null(&desc->chunk_list, struct dw_edma_chunk, list); if (!child) return 0; dw_edma_core_start(child, !desc->xfer_sz); desc->xfer_sz += child->xfer_sz; - dw_edma_free_burst(child); list_del(&child->list); kfree(child); desc->chunks_alloc--; @@ -425,14 +355,14 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer, struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan); enum dma_transfer_direction dir = xfer->direction; struct scatterlist *sg = NULL; - struct dw_edma_chunk *chunk; + struct dw_edma_chunk *chunk = NULL; struct dw_edma_burst *burst; struct dw_edma_desc *desc; u64 src_addr, dst_addr; size_t fsz = 0; u32 bursts_max; u32 cnt = 0; - int i; + u32 i; if (!chan->configured) return NULL; @@ -499,10 +429,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer, if (unlikely(!desc)) goto err_alloc; - chunk = dw_edma_alloc_chunk(desc); - if (unlikely(!chunk)) - goto err_alloc; - if (xfer->type == EDMA_XFER_INTERLEAVED) { src_addr = xfer->xfer.il->src_start; dst_addr = xfer->xfer.il->dst_start; @@ -530,15 +456,15 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer, if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg) break; - if (chunk->bursts_alloc == bursts_max) { - chunk = dw_edma_alloc_chunk(desc); + if (!(i % bursts_max)) { + u32 n = min(cnt - i, bursts_max); + + chunk = dw_edma_alloc_chunk(desc, n); if (unlikely(!chunk)) goto err_alloc; } - burst = dw_edma_alloc_burst(chunk); - if (unlikely(!burst)) - goto err_alloc; + burst = chunk->burst + (i % bursts_max); if (xfer->type == EDMA_XFER_CYCLIC) burst->sz = xfer->xfer.cyclic.len; diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 27415f3a2d04..4950c57fca34 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -43,7 +43,6 @@ struct dw_edma_chan; struct dw_edma_chunk; struct dw_edma_burst { - struct list_head list; u64 sar; u64 dar; u32 sz; @@ -52,18 +51,16 @@ struct dw_edma_burst { struct dw_edma_chunk { struct list_head list; struct dw_edma_chan *chan; - struct dw_edma_burst *burst; - - u32 bursts_alloc; - u8 cb; u32 xfer_sz; + u32 nburst; + struct dw_edma_burst burst[] __counted_by(nburst); }; struct dw_edma_desc { struct virt_dma_desc vd; struct dw_edma_chan *chan; - struct dw_edma_chunk *chunk; + struct list_head chunk_list; u32 chunks_alloc;