This patch adds prep_dma_sg function to transfer memory to memory which mapped in scatter/gather list. The patch move get_burst_len to upwards to call in the __pl330_prep_dma_mecpy. Some duplicated code was splitted off from prep_dma_memcpy. This patch also included previous Boojon Kim's patch[1] which fixes burst_size calculation. The burst_size should be aligned not only length but also source and destionation addresses. [1] : http://lkml.indiana.edu/hypermail/linux/kernel/1201.1/00246.html Cc: Boojin Kim <boojin.kim@xxxxxxxxxxx> Signed-off-by: Chanho Park <chanho61.park@xxxxxxxxxxx> Acked-by: Jassi Brar <jassisinghbrar@xxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- drivers/dma/pl330.c | 179 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 138 insertions(+), 41 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index a562d24..b272ee6 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2602,9 +2602,33 @@ static inline void fill_px(struct pl330_xfer *px, px->src_addr = src; } +/* Call after fixing burst size */ +static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) +{ + struct dma_pl330_chan *pch = desc->pchan; + struct pl330_info *pi = &pch->dmac->pif; + int burst_len; + + burst_len = pi->pcfg.data_bus_width / 8; + burst_len *= pi->pcfg.data_buf_dep; + burst_len >>= desc->rqcfg.brst_size; + + /* src/dst_burst_len can't be more than 16 */ + if (burst_len > 16) + burst_len = 16; + + while (burst_len > 1) { + if (!(len % (burst_len << desc->rqcfg.brst_size))) + break; + burst_len--; + } + + return burst_len; +} + static struct dma_pl330_desc * __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst, - dma_addr_t src, size_t len) + dma_addr_t src, size_t len, int burst) { struct dma_pl330_desc *desc = pl330_get_desc(pch); @@ -2626,31 +2650,23 @@ __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst, */ fill_px(&desc->px, dst, src, len); - return desc; -} - -/* Call after fixing burst size */ -static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) -{ - struct dma_pl330_chan *pch = desc->pchan; - struct pl330_info *pi = &pch->dmac->pif; - int burst_len; - - burst_len = pi->pcfg.data_bus_width / 8; - burst_len *= pi->pcfg.data_buf_dep; - burst_len >>= desc->rqcfg.brst_size; - - /* src/dst_burst_len can't be more than 16 */ - if (burst_len > 16) - burst_len = 16; + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 1; + desc->req.rqtype = MEMTOMEM; - while (burst_len > 1) { - if (!(len % (burst_len << desc->rqcfg.brst_size))) + while (burst > 1) { + if (!(len % burst) && !(len % dst) && !(len % src)) break; - burst_len--; + burst /= 2; } - return burst_len; + desc->rqcfg.brst_size = 0; + while (burst != (1 << desc->rqcfg.brst_size)) + desc->rqcfg.brst_size++; + + desc->rqcfg.brst_len = get_burst_len(desc, len); + + return desc; } static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( @@ -2752,28 +2768,12 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, pi = &pch->dmac->pif; - desc = __pl330_prep_dma_memcpy(pch, dst, src, len); - if (!desc) - return NULL; - - desc->rqcfg.src_inc = 1; - desc->rqcfg.dst_inc = 1; - desc->req.rqtype = MEMTOMEM; - /* Select max possible burst size */ burst = pi->pcfg.data_bus_width / 8; - while (burst > 1) { - if (!(len % burst)) - break; - burst /= 2; - } - - desc->rqcfg.brst_size = 0; - while (burst != (1 << desc->rqcfg.brst_size)) - desc->rqcfg.brst_size++; - - desc->rqcfg.brst_len = get_burst_len(desc, len); + desc = __pl330_prep_dma_memcpy(pch, dst, src, len, burst); + if (!desc) + return NULL; desc->txd.flags = flags; @@ -2803,6 +2803,102 @@ static void __pl330_giveback_desc(struct dma_pl330_dmac *pdmac, } static struct dma_async_tx_descriptor * +pl330_prep_dma_sg(struct dma_chan *chan, + struct scatterlist *dst_sg, unsigned int dst_nents, + struct scatterlist *src_sg, unsigned int src_nents, + unsigned long flags) +{ + struct dma_pl330_desc *first, *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + struct pl330_info *pi; + dma_addr_t src, dst; + size_t len, dst_len = 0, src_len = 0; + int burst; + + if (unlikely(!pch)) + return NULL; + + pi = &pch->dmac->pif; + + /* basic sanity checks */ + if (dst_nents == 0 || src_nents == 0) + return NULL; + + if (dst_sg == NULL || src_sg == NULL) + return NULL; + + first = NULL; + + /* Select max possible burst size */ + burst = pi->pcfg.data_bus_width / 8; + + /* get prepared for the loop */ + dst_len = sg_dma_len(dst_sg); + src_len = sg_dma_len(src_sg); + + while (true) { + len = min_t(size_t, src_len, dst_len); + + if (len == 0) + goto fetch; + + dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_len; + src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_len; + + desc = __pl330_prep_dma_memcpy(pch, dst, src, len, burst); + if (!desc) { + struct dma_pl330_dmac *pdmac = pch->dmac; + + dev_err(pdmac->pif.dev, + "%s:%d Unable to fetch desc\n", + __func__, __LINE__); + + __pl330_giveback_desc(pdmac, first); + + return NULL; + } + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->node); + + desc->txd.flags = flags; + + dst_len -= len; + src_len -= len; + +fetch: + /* fetch the next dst scatterlist entry */ + if (dst_len == 0) { + /* no more entries: we're done */ + if (dst_nents == 0) + break; + /* fetch the next entry: if there are no more: done */ + dst_sg = sg_next(dst_sg); + if (dst_sg == NULL) + break; + dst_nents--; + dst_len = sg_dma_len(dst_sg); + } + + /* fetch the next src scatterlist entry */ + if (src_len == 0) { + /* no more entries: we're done */ + if (src_nents == 0) + break; + /* fetch the next entry: if there are no more: done */ + src_sg = sg_next(src_sg); + if (src_sg == NULL) + break; + src_nents--; + src_len = sg_dma_len(src_sg); + } + } + + return &first->txd; +} + +static struct dma_async_tx_descriptor * pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flg, void *context) @@ -2989,6 +3085,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; + pd->device_prep_dma_sg = pl330_prep_dma_sg; pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html