diff --git a/packages/multimedia/rkmpp/patches/rkmpp-1000-hevc.patch b/packages/multimedia/rkmpp/patches/rkmpp-1000-hevc.patch new file mode 100644 index 0000000000..397b1bec94 --- /dev/null +++ b/packages/multimedia/rkmpp/patches/rkmpp-1000-hevc.patch @@ -0,0 +1,38 @@ +diff --git a/mpp/hal/rkdec/h265d/hal_h265d_reg.c b/mpp/hal/rkdec/h265d/hal_h265d_reg.c +index a60d6c2..3d4265b 100644 +--- a/mpp/hal/rkdec/h265d/hal_h265d_reg.c ++++ b/mpp/hal/rkdec/h265d/hal_h265d_reg.c +@@ -1468,8 +1468,6 @@ MPP_RET hal_h265d_gen_regs(void *hal, HalTaskInfo *syn) + } + hw_regs->sw_interrupt.sw_dec_e = 1; + hw_regs->sw_interrupt.sw_dec_timeout_e = 1; +- hw_regs->sw_interrupt.sw_wr_ddr_align_en = dxva_cxt->pp.tiles_enabled_flag +- ? 0 : 1; + + + ///find s->rps_model[i] position, and set register +diff --git a/mpp/hal/rkdec/h265d/hal_h265d_reg.h b/mpp/hal/rkdec/h265d/hal_h265d_reg.h +index 1bccb02..432b8db 100644 +--- a/mpp/hal/rkdec/h265d/hal_h265d_reg.h ++++ b/mpp/hal/rkdec/h265d/hal_h265d_reg.h +@@ -50,7 +50,8 @@ typedef struct { + struct swreg_int { + RK_U32 sw_dec_e : 1 ; + RK_U32 sw_dec_clkgate_e : 1 ; +- RK_U32 reserve0 : 2 ; ++ RK_U32 reserve0 : 1 ; ++ RK_U32 sw_timeout_mode : 1 ; + RK_U32 sw_dec_irq_dis : 1 ; + RK_U32 sw_dec_timeout_e : 1 ; + RK_U32 sw_buf_empty_en : 1 ; +@@ -61,8 +62,9 @@ typedef struct { + RK_U32 sw_dec_rdy_sta : 1 ; + RK_U32 sw_dec_bus_sta : 1 ; + RK_U32 sw_dec_error_sta : 1 ; ++ RK_U32 sw_dec_timeout_sta : 1 ; + RK_U32 sw_dec_empty_sta : 1 ; +- RK_U32 reserve4 : 4 ; ++ RK_U32 reserve3 : 3 ; + RK_U32 sw_softrst_en_p : 1 ; + RK_U32 sw_force_softreset_valid: 1 ; + RK_U32 sw_softreset_rdy : 1 ; diff --git a/projects/Rockchip/patches/linux/rockchip-4.4/linux-1000-pl330.patch b/projects/Rockchip/patches/linux/rockchip-4.4/linux-1000-pl330.patch new file mode 100644 index 0000000000..e83ca8ca7d --- /dev/null +++ b/projects/Rockchip/patches/linux/rockchip-4.4/linux-1000-pl330.patch @@ -0,0 +1,1615 @@ +From c5762affa4dc61b624b0440a4b4846550a305552 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Sat, 21 Oct 2017 19:49:27 +0200 +Subject: [PATCH 01/16] Revert "dmaengine: pl330: _loop_cyclic fix cycles of + last loop" + +This reverts commit d7155171cbc65e45b5b0c8db03fd16fa57a181f2. +--- + drivers/dma/pl330.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index cd05495735d7..1e5c79b20c5b 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1366,7 +1366,7 @@ static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned dry_run, + ccr &= ~(0xf << CC_SRCBRSTLEN_SHFT); + ccr &= ~(0xf << CC_DSTBRSTLEN_SHFT); + off += _emit_MOV(dry_run, &buf[off], CCR, ccr); +- off += _emit_LP(dry_run, &buf[off], 1, c); ++ off += _emit_LP(dry_run, &buf[off], 1, c - 1); + ljmp1 = off; + off += _bursts(pl330, dry_run, &buf[off], pxs, 1); + lpend.cond = ALWAYS; + +From 7d241ca0d9e3c658f92c54614cf50812557fb899 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Fri, 16 Jun 2017 23:14:54 +0200 +Subject: [PATCH 02/16] Revert "dmaengine: pl330: _loop_cyclic supports + unaligned size" + +This reverts commit 13dbe2cccd5851540af8158b12499c33801b6ef6. +--- + drivers/dma/pl330.c | 38 ++++++++++---------------------------- + 1 file changed, 10 insertions(+), 28 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 1e5c79b20c5b..4661dfb1a195 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1356,28 +1356,6 @@ static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned dry_run, + off += _emit_LPEND(dry_run, &buf[off], &lpend); + } + +- if (pl330->peripherals_req_type == BURST) { +- unsigned int ccr = pxs->ccr; +- unsigned long c = 0; +- +- c = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); +- +- if (c) { +- ccr &= ~(0xf << CC_SRCBRSTLEN_SHFT); +- ccr &= ~(0xf << CC_DSTBRSTLEN_SHFT); +- off += _emit_MOV(dry_run, &buf[off], CCR, ccr); +- off += _emit_LP(dry_run, &buf[off], 1, c - 1); +- ljmp1 = off; +- off += _bursts(pl330, dry_run, &buf[off], pxs, 1); +- lpend.cond = ALWAYS; +- lpend.forever = false; +- lpend.loop = 1; +- lpend.bjump = off - ljmp1; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); +- } +- } +- + off += _emit_SEV(dry_run, &buf[off], ev); + + lpend.cond = ALWAYS; +@@ -1479,13 +1457,13 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + + x = &pxs->desc->px; + +- if (pl330->peripherals_req_type != BURST) { +- /* Error if xfer length is not aligned at burst size */ +- if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) +- return -EINVAL; +- } +- + if (!pxs->desc->cyclic) { ++ if (pl330->peripherals_req_type != BURST) { ++ /* Error if xfer length is not aligned at burst size */ ++ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) ++ return -EINVAL; ++ } ++ + off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + + /* DMASEV peripheral/event */ +@@ -1493,6 +1471,10 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + } else { ++ /* Error if xfer length is not aligned at burst size */ ++ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) ++ return -EINVAL; ++ + off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], + pxs, thrd->ev); + } + +From a21f49560bf8cf38b5832a0f37d4c16610bec2df Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Fri, 16 Jun 2017 23:14:54 +0200 +Subject: [PATCH 03/16] Revert "dmaengine: pl330: redefine the cyclic transfer" + +This reverts commit 5f638786e66089344c9cf594b81fbf02cd794f15. +--- + drivers/dma/pl330.c | 137 +++++++++++----------------------------------------- + 1 file changed, 29 insertions(+), 108 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 4661dfb1a195..d8912a4216df 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1303,76 +1303,6 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], + return off; + } + +-/* Returns bytes consumed */ +-static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned dry_run, +- u8 buf[], unsigned long bursts, const struct _xfer_spec *pxs, int ev) +-{ +- int cyc, off; +- unsigned lcnt0, lcnt1, ljmp0, ljmp1, ljmpfe; +- struct _arg_LPEND lpend; +- struct pl330_xfer *x = &pxs->desc->px; +- +- off = 0; +- ljmpfe = off; +- lcnt0 = pxs->desc->num_periods; +- +- if (bursts > 256) { +- lcnt1 = 256; +- cyc = bursts / 256; +- } else { +- lcnt1 = bursts; +- cyc = 1; +- } +- +- /* forever loop */ +- off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); +- off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); +- +- /* loop0 */ +- off += _emit_LP(dry_run, &buf[off], 0, lcnt0); +- ljmp0 = off; +- +- /* loop1 */ +- off += _emit_LP(dry_run, &buf[off], 1, lcnt1); +- ljmp1 = off; +- off += _bursts(pl330, dry_run, &buf[off], pxs, cyc); +- lpend.cond = ALWAYS; +- lpend.forever = false; +- lpend.loop = 1; +- lpend.bjump = off - ljmp1; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- +- /* remainder */ +- lcnt1 = bursts - (lcnt1 * cyc); +- +- if (lcnt1) { +- off += _emit_LP(dry_run, &buf[off], 1, lcnt1); +- ljmp1 = off; +- off += _bursts(pl330, dry_run, &buf[off], pxs, 1); +- lpend.cond = ALWAYS; +- lpend.forever = false; +- lpend.loop = 1; +- lpend.bjump = off - ljmp1; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- } +- +- off += _emit_SEV(dry_run, &buf[off], ev); +- +- lpend.cond = ALWAYS; +- lpend.forever = false; +- lpend.loop = 0; +- lpend.bjump = off - ljmp0; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- +- lpend.cond = ALWAYS; +- lpend.forever = true; +- lpend.loop = 1; +- lpend.bjump = off - ljmpfe; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- +- return off; +-} +- + static inline int _setup_loops(struct pl330_dmac *pl330, + unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) +@@ -1392,16 +1322,19 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + } + + static inline int _setup_xfer(struct pl330_dmac *pl330, +- unsigned dry_run, u8 buf[], ++ unsigned dry_run, u8 buf[], u32 period, + const struct _xfer_spec *pxs) + { + struct pl330_xfer *x = &pxs->desc->px; ++ struct pl330_reqcfg *rqcfg = &pxs->desc->rqcfg; + int off = 0; + + /* DMAMOV SAR, x->src_addr */ +- off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); ++ off += _emit_MOV(dry_run, &buf[off], SAR, ++ x->src_addr + rqcfg->src_inc * period * x->bytes); + /* DMAMOV DAR, x->dst_addr */ +- off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); ++ off += _emit_MOV(dry_run, &buf[off], DAR, ++ x->dst_addr + rqcfg->dst_inc * period * x->bytes); + + /* Setup Loop(s) */ + off += _setup_loops(pl330, dry_run, &buf[off], pxs); +@@ -1423,20 +1356,6 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + return off; + } + +-static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, unsigned dry_run, +- u8 buf[], const struct _xfer_spec *pxs, int ev) +-{ +- struct pl330_xfer *x = &pxs->desc->px; +- u32 ccr = pxs->ccr; +- unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); +- int off = 0; +- +- /* Setup Loop(s) */ +- off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); +- +- return off; +-} +- + /* + * A req is a sequence of one or more xfer units. + * Returns the number of bytes taken to setup the MC for the req. +@@ -1449,34 +1368,42 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + struct pl330_xfer *x; + u8 *buf = req->mc_cpu; + int off = 0; ++ int period; ++ int again_off; + + PL330_DBGMC_START(req->mc_bus); + + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); ++ again_off = off; + + x = &pxs->desc->px; ++ if (pl330->peripherals_req_type != BURST) { ++ /* Error if xfer length is not aligned at burst size */ ++ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) ++ return -EINVAL; ++ } + +- if (!pxs->desc->cyclic) { +- if (pl330->peripherals_req_type != BURST) { +- /* Error if xfer length is not aligned at burst size */ +- if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) +- return -EINVAL; +- } +- +- off += _setup_xfer(pl330, dry_run, &buf[off], pxs); ++ for (period = 0; period < pxs->desc->num_periods; period++) { ++ off += _setup_xfer(pl330, dry_run, &buf[off], period, pxs); + + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ } ++ ++ if (!pxs->desc->cyclic) { + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); + } else { +- /* Error if xfer length is not aligned at burst size */ +- if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) +- return -EINVAL; +- +- off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], +- pxs, thrd->ev); ++ struct _arg_LPEND lpend; ++ /* LP */ ++ off += _emit_LP(dry_run, &buf[off], 0, 255); ++ /* LPEND */ ++ lpend.cond = ALWAYS; ++ lpend.forever = false; ++ lpend.loop = 0; ++ lpend.bjump = off - again_off; ++ off += _emit_LPEND(dry_run, &buf[off], &lpend); + } + + return off; +@@ -2656,7 +2583,6 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + { + struct dma_pl330_desc *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); +- struct pl330_dmac *pl330 = pch->dmac; + dma_addr_t dst; + dma_addr_t src; + +@@ -2695,12 +2621,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + + desc->rqtype = direction; + desc->rqcfg.brst_size = pch->burst_sz; +- +- if (pl330->peripherals_req_type == BURST) +- desc->rqcfg.brst_len = pch->burst_len; +- else +- desc->rqcfg.brst_len = 1; +- ++ desc->rqcfg.brst_len = pch->burst_len; + desc->bytes_requested = len; + fill_px(&desc->px, dst, src, period_len); + + +From 7cd3fa397393d461d94167554653ef168b8e703d Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Fri, 16 Jun 2017 23:14:54 +0200 +Subject: [PATCH 04/16] Revert "dmaengine: pl330: make transfer run infinitely + without CPU intervention" + +This reverts commit e8a6e5086cb82d59cae6ae029b1eb4432cc62288. +--- + drivers/dma/pl330.c | 199 +++++++++++++++++++++++++++------------------------- + 1 file changed, 105 insertions(+), 94 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index d8912a4216df..58e99ae039f8 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -447,6 +447,9 @@ struct dma_pl330_chan { + int burst_len; /* the number of burst */ + dma_addr_t fifo_addr; + ++ /* for cyclic capability */ ++ bool cyclic; ++ + /* for runtime pm tracking */ + bool active; + }; +@@ -532,10 +535,6 @@ struct dma_pl330_desc { + unsigned peri:5; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; +- +- /* For cyclic capability */ +- bool cyclic; +- size_t num_periods; + }; + + struct _xfer_spec { +@@ -1322,19 +1321,16 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + } + + static inline int _setup_xfer(struct pl330_dmac *pl330, +- unsigned dry_run, u8 buf[], u32 period, ++ unsigned dry_run, u8 buf[], + const struct _xfer_spec *pxs) + { + struct pl330_xfer *x = &pxs->desc->px; +- struct pl330_reqcfg *rqcfg = &pxs->desc->rqcfg; + int off = 0; + + /* DMAMOV SAR, x->src_addr */ +- off += _emit_MOV(dry_run, &buf[off], SAR, +- x->src_addr + rqcfg->src_inc * period * x->bytes); ++ off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); + /* DMAMOV DAR, x->dst_addr */ +- off += _emit_MOV(dry_run, &buf[off], DAR, +- x->dst_addr + rqcfg->dst_inc * period * x->bytes); ++ off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); + + /* Setup Loop(s) */ + off += _setup_loops(pl330, dry_run, &buf[off], pxs); +@@ -1368,14 +1364,11 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + struct pl330_xfer *x; + u8 *buf = req->mc_cpu; + int off = 0; +- int period; +- int again_off; + + PL330_DBGMC_START(req->mc_bus); + + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); +- again_off = off; + + x = &pxs->desc->px; + if (pl330->peripherals_req_type != BURST) { +@@ -1384,27 +1377,12 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + return -EINVAL; + } + +- for (period = 0; period < pxs->desc->num_periods; period++) { +- off += _setup_xfer(pl330, dry_run, &buf[off], period, pxs); +- +- /* DMASEV peripheral/event */ +- off += _emit_SEV(dry_run, &buf[off], thrd->ev); +- } ++ off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + +- if (!pxs->desc->cyclic) { +- /* DMAEND */ +- off += _emit_END(dry_run, &buf[off]); +- } else { +- struct _arg_LPEND lpend; +- /* LP */ +- off += _emit_LP(dry_run, &buf[off], 0, 255); +- /* LPEND */ +- lpend.cond = ALWAYS; +- lpend.forever = false; +- lpend.loop = 0; +- lpend.bjump = off - again_off; +- off += _emit_LPEND(dry_run, &buf[off], &lpend); +- } ++ /* DMASEV peripheral/event */ ++ off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ /* DMAEND */ ++ off += _emit_END(dry_run, &buf[off]); + + return off; + } +@@ -1666,13 +1644,12 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; ++ thrd->req[active].desc = NULL; + +- if (!descdone->cyclic) { +- thrd->req[active].desc = NULL; +- thrd->req_running = -1; +- /* Get going again ASAP */ +- _start(thrd); +- } ++ thrd->req_running = -1; ++ ++ /* Get going again ASAP */ ++ _start(thrd); + + /* For now, just make a list of callbacks to be done */ + list_add_tail(&descdone->rqd, &pl330->req_done); +@@ -2051,27 +2028,12 @@ static void pl330_tasklet(unsigned long data) + spin_lock_irqsave(&pch->lock, flags); + + /* Pick up ripe tomatoes */ +- list_for_each_entry_safe(desc, _dt, &pch->work_list, node) { ++ list_for_each_entry_safe(desc, _dt, &pch->work_list, node) + if (desc->status == DONE) { +- if (!desc->cyclic) { ++ if (!pch->cyclic) + dma_cookie_complete(&desc->txd); +- list_move_tail(&desc->node, &pch->completed_list); +- } else { +- dma_async_tx_callback callback; +- void *callback_param; +- +- desc->status = BUSY; +- callback = desc->txd.callback; +- callback_param = desc->txd.callback_param; +- +- if (callback) { +- spin_unlock_irqrestore(&pch->lock, flags); +- callback(callback_param); +- spin_lock_irqsave(&pch->lock, flags); +- } +- } ++ list_move_tail(&desc->node, &pch->completed_list); + } +- } + + /* Try to submit a req imm. next to the last completed cookie */ + fill_queue(pch); +@@ -2099,8 +2061,20 @@ static void pl330_tasklet(unsigned long data) + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + +- desc->status = FREE; +- list_move_tail(&desc->node, &pch->dmac->desc_pool); ++ if (pch->cyclic) { ++ desc->status = PREP; ++ list_move_tail(&desc->node, &pch->work_list); ++ if (power_down) { ++ pch->active = true; ++ spin_lock(&pch->thread->dmac->lock); ++ _start(pch->thread); ++ spin_unlock(&pch->thread->dmac->lock); ++ power_down = false; ++ } ++ } else { ++ desc->status = FREE; ++ list_move_tail(&desc->node, &pch->dmac->desc_pool); ++ } + + dma_descriptor_unmap(&desc->txd); + +@@ -2160,6 +2134,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + spin_lock_irqsave(&pch->lock, flags); + + dma_cookie_init(chan); ++ pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +@@ -2282,7 +2257,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + pl330_release_channel(pch->thread); + pch->thread = NULL; + +- list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); ++ if (pch->cyclic) ++ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + + spin_unlock_irqrestore(&pch->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); +@@ -2336,7 +2312,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + + /* Check in pending list */ + list_for_each_entry(desc, &pch->work_list, node) { +- if (desc->status == DONE && !desc->cyclic) ++ if (desc->status == DONE) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = +@@ -2408,8 +2384,12 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) + /* Assign cookies to all nodes */ + while (!list_empty(&last->node)) { + desc = list_entry(last->node.next, struct dma_pl330_desc, node); +- ++ if (pch->cyclic) { ++ desc->txd.callback = last->txd.callback; ++ desc->txd.callback_param = last->txd.callback_param; ++ } + desc->last = false; ++ + dma_cookie_assign(&desc->txd); + + list_move_tail(&desc->node, &pch->submitted_list); +@@ -2509,9 +2489,6 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) + desc->peri = peri_id ? pch->chan.chan_id : 0; + desc->rqcfg.pcfg = &pch->dmac->pcfg; + +- desc->cyclic = false; +- desc->num_periods = 1; +- + dma_async_tx_descriptor_init(&desc->txd, &pch->chan); + + return desc; +@@ -2581,8 +2558,10 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) + { +- struct dma_pl330_desc *desc = NULL; ++ struct dma_pl330_desc *desc = NULL, *first = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); ++ struct pl330_dmac *pl330 = pch->dmac; ++ unsigned int i; + dma_addr_t dst; + dma_addr_t src; + +@@ -2595,38 +2574,70 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + return NULL; + } + +- desc = pl330_get_desc(pch); +- if (!desc) { +- dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", +- __func__, __LINE__); +- return NULL; +- } ++ for (i = 0; i < len / period_len; i++) { ++ desc = pl330_get_desc(pch); ++ if (!desc) { ++ dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", ++ __func__, __LINE__); + +- switch (direction) { +- case DMA_MEM_TO_DEV: +- desc->rqcfg.src_inc = 1; +- desc->rqcfg.dst_inc = 0; +- src = dma_addr; +- dst = pch->fifo_addr; +- break; +- case DMA_DEV_TO_MEM: +- desc->rqcfg.src_inc = 0; +- desc->rqcfg.dst_inc = 1; +- src = pch->fifo_addr; +- dst = dma_addr; +- break; +- default: +- break; ++ if (!first) ++ return NULL; ++ ++ spin_lock_irqsave(&pl330->pool_lock, flags); ++ ++ while (!list_empty(&first->node)) { ++ desc = list_entry(first->node.next, ++ struct dma_pl330_desc, node); ++ list_move_tail(&desc->node, &pl330->desc_pool); ++ } ++ ++ list_move_tail(&first->node, &pl330->desc_pool); ++ ++ spin_unlock_irqrestore(&pl330->pool_lock, flags); ++ ++ return NULL; ++ } ++ ++ switch (direction) { ++ case DMA_MEM_TO_DEV: ++ desc->rqcfg.src_inc = 1; ++ desc->rqcfg.dst_inc = 0; ++ src = dma_addr; ++ dst = pch->fifo_addr; ++ break; ++ case DMA_DEV_TO_MEM: ++ desc->rqcfg.src_inc = 0; ++ desc->rqcfg.dst_inc = 1; ++ src = pch->fifo_addr; ++ dst = dma_addr; ++ break; ++ default: ++ break; ++ } ++ ++ desc->rqtype = direction; ++ desc->rqcfg.brst_size = pch->burst_sz; ++ ++ if (pl330->peripherals_req_type == BURST) ++ desc->rqcfg.brst_len = pch->burst_len; ++ else ++ desc->rqcfg.brst_len = 1; ++ ++ desc->bytes_requested = period_len; ++ fill_px(&desc->px, dst, src, period_len); ++ ++ if (!first) ++ first = desc; ++ else ++ list_add_tail(&desc->node, &first->node); ++ ++ dma_addr += period_len; + } + +- desc->rqtype = direction; +- desc->rqcfg.brst_size = pch->burst_sz; +- desc->rqcfg.brst_len = pch->burst_len; +- desc->bytes_requested = len; +- fill_px(&desc->px, dst, src, period_len); ++ if (!desc) ++ return NULL; + +- desc->cyclic = true; +- desc->num_periods = len / period_len; ++ pch->cyclic = true; + desc->txd.flags = flags; + + return &desc->txd; + +From 5e0a2880602942d120459525a752e86533300c44 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Fri, 16 Jun 2017 23:14:55 +0200 +Subject: [PATCH 05/16] Revert "dmaengine: pl330: support transfer that doesn't + align with (burst len * burst size)" + +This reverts commit c66ecf19b98ffac86177c29859e683de39f44e73. +--- + drivers/dma/pl330.c | 23 +++-------------------- + 1 file changed, 3 insertions(+), 20 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 58e99ae039f8..f4837f5b4e87 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -240,7 +240,6 @@ enum pl330_byteswap { + + #define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) + #define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) +-#define BYTE_MOD_BURST_LEN(b, ccr) (((b) / BRST_SIZE(ccr)) % BRST_LEN(ccr)) + + /* + * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req +@@ -1335,20 +1334,6 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + /* Setup Loop(s) */ + off += _setup_loops(pl330, dry_run, &buf[off], pxs); + +- if (pl330->peripherals_req_type == BURST) { +- unsigned int ccr = pxs->ccr; +- unsigned long c = 0; +- +- c = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); +- +- if (c) { +- ccr &= ~(0xf << CC_SRCBRSTLEN_SHFT); +- ccr &= ~(0xf << CC_DSTBRSTLEN_SHFT); +- off += _emit_MOV(dry_run, &buf[off], CCR, ccr); +- off += _loop(pl330, dry_run, &buf[off], &c, pxs); +- } +- } +- + return off; + } + +@@ -1371,11 +1356,9 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + + x = &pxs->desc->px; +- if (pl330->peripherals_req_type != BURST) { +- /* Error if xfer length is not aligned at burst size */ +- if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) +- return -EINVAL; +- } ++ /* Error if xfer length is not aligned at burst size */ ++ if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) ++ return -EINVAL; + + off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + + +From fd77628162b4a13bc54a9187dc25fbc65ad79f37 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Fri, 16 Jun 2017 23:14:55 +0200 +Subject: [PATCH 06/16] Revert "dmaengine: pl330: add burst mode according to + dts config" + +This reverts commit 8e770f371cc27f8828cb9ceb0516adc23fe75995. +--- + drivers/dma/pl330.c | 36 ++++++++++++++---------------------- + 1 file changed, 14 insertions(+), 22 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index f4837f5b4e87..778be86e908c 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -494,8 +494,6 @@ struct pl330_dmac { + /* Peripheral channels connected to this DMAC */ + unsigned int num_peripherals; + struct dma_pl330_chan *peripherals; /* keep at end */ +- /* set peripherals request type according to soc config*/ +- enum pl330_cond peripherals_req_type; + int quirks; + }; + +@@ -1161,7 +1159,12 @@ static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run, + int cyc) + { + int off = 0; +- enum pl330_cond cond = pl330->peripherals_req_type; ++ enum pl330_cond cond; ++ ++ if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ++ cond = BURST; ++ else ++ cond = SINGLE; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +@@ -1181,7 +1184,12 @@ static inline int _ldst_memtodev(struct pl330_dmac *pl330, + const struct _xfer_spec *pxs, int cyc) + { + int off = 0; +- enum pl330_cond cond = pl330->peripherals_req_type; ++ enum pl330_cond cond; ++ ++ if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ++ cond = BURST; ++ else ++ cond = SINGLE; + + while (cyc--) { + off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +@@ -2600,12 +2608,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + + desc->rqtype = direction; + desc->rqcfg.brst_size = pch->burst_sz; +- +- if (pl330->peripherals_req_type == BURST) +- desc->rqcfg.brst_len = pch->burst_len; +- else +- desc->rqcfg.brst_len = 1; +- ++ desc->rqcfg.brst_len = 1; + desc->bytes_requested = period_len; + fill_px(&desc->px, dst, src, period_len); + +@@ -2707,7 +2710,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + { + struct dma_pl330_desc *first, *desc = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); +- struct pl330_dmac *pl330 = pch->dmac; + struct scatterlist *sg; + int i; + dma_addr_t addr; +@@ -2751,12 +2753,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + } + + desc->rqcfg.brst_size = pch->burst_sz; +- +- if (pl330->peripherals_req_type == BURST) +- desc->rqcfg.brst_len = pch->burst_len; +- else +- desc->rqcfg.brst_len = 1; +- ++ desc->rqcfg.brst_len = 1; + desc->rqtype = direction; + desc->bytes_requested = sg_dma_len(sg); + } +@@ -2852,11 +2849,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + + pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0; + +- if (of_find_property(np, "peripherals-req-type-burst", NULL)) +- pl330->peripherals_req_type = BURST; +- else +- pl330->peripherals_req_type = SINGLE; +- + /* get quirk */ + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) + if (of_property_read_bool(np, of_quirks[i].quirk)) + +From 754e41a208e456b127896f0c017ca523d7e93ccc Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Tue, 5 Jul 2016 10:02:16 +0530 +Subject: [PATCH 07/16] UPSTREAM: dmaengine: pl330: explicitly freeup irq + +dmaengine device should explicitly call devm_free_irq() when using +devm_request_irq(). + +The irq is still ON when devices remove is executed and irq should be +quiesced before remove is completed. + +Signed-off-by: Vinod Koul +Cc: Jassi Brar +Cc: Linus Walleij +(cherry picked from commit 46cf94d6ab38420690d890d9922bfc61a7b3e2c5) +--- + drivers/dma/pl330.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 778be86e908c..4e6590f90994 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -3016,12 +3016,18 @@ static int pl330_remove(struct amba_device *adev) + { + struct pl330_dmac *pl330 = amba_get_drvdata(adev); + struct dma_pl330_chan *pch, *_p; ++ int i, irq; + + pm_runtime_get_noresume(pl330->ddma.dev); + + if (adev->dev.of_node) + of_dma_controller_free(adev->dev.of_node); + ++ for (i = 0; i < AMBA_NR_IRQS; i++) { ++ irq = adev->irq[i]; ++ devm_free_irq(&adev->dev, irq, pl330); ++ } ++ + dma_async_device_unregister(&pl330->ddma); + + /* Idle the DMAC */ + +From d486322e47fdaaf0ba14c5dc1a3bad97400814b0 Mon Sep 17 00:00:00 2001 +From: Stephen Barber +Date: Thu, 18 Aug 2016 17:59:59 -0700 +Subject: [PATCH 08/16] UPSTREAM: dmaengine: pl330: fix residual for + non-running BUSY descriptors + +Only one descriptor in the work list should be running at +any given time, but it's possible to have an enqueued BUSY +descriptor that has not yet transferred any data, or for +a BUSY descriptor to linger briefly before transitioning +to DONE. These cases should be handled to keep residual +calculations consistent even with the non-running BUSY +descriptors in the work list. + +Signed-off-by: Stephen Barber +Signed-off-by: Vinod Koul +(cherry picked from commit d64e9a2c750930272492952c16f3f2c95311a6c9) +--- + drivers/dma/pl330.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 4e6590f90994..16f578f1d050 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -2284,7 +2284,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + { + enum dma_status ret; + unsigned long flags; +- struct dma_pl330_desc *desc, *running = NULL; ++ struct dma_pl330_desc *desc, *running = NULL, *last_enq = NULL; + struct dma_pl330_chan *pch = to_pchan(chan); + unsigned int transferred, residual = 0; + +@@ -2301,6 +2301,8 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + if (pch->thread->req_running != -1) + running = pch->thread->req[pch->thread->req_running].desc; + ++ last_enq = pch->thread->req[pch->thread->lstenq].desc; ++ + /* Check in pending list */ + list_for_each_entry(desc, &pch->work_list, node) { + if (desc->status == DONE) +@@ -2308,6 +2310,15 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + else if (running && desc == running) + transferred = + pl330_get_current_xferred_count(pch, desc); ++ else if (desc->status == BUSY) ++ /* ++ * Busy but not running means either just enqueued, ++ * or finished and not yet marked done ++ */ ++ if (desc == last_enq) ++ transferred = 0; ++ else ++ transferred = desc->bytes_requested; + else + transferred = 0; + residual += desc->bytes_requested - transferred; + +From be63b825e6305654e11be4313e083fbafc09825c Mon Sep 17 00:00:00 2001 +From: Hsin-Yu Chao +Date: Tue, 23 Aug 2016 17:16:55 +0800 +Subject: [PATCH 09/16] UPSTREAM: dmaengine: pl330: Acquire dmac's spinlock in + pl330_tx_status + +There is a racing when accessing dmac thread in pl330_tx_status that +the pl330_update is handling active request at the same time and +changing the status of descriptors. This could cause an invalid +transferred count from BUSY descriptor added up to the residual number. +Fix the bug by using the dmac's spinlock in pl330_tx_status to protect +thread resources from changing. +Note that the nested order of holding dmac's and dma_chan's spinlock is +consistent with the rest of the driver: dma_chan first and then dmac, +so it is safe from deadlock scenario. + +Signed-off-by: Hsin-Yu Chao +Reviewed-by: Guenter Roeck +Signed-off-by: Vinod Koul +(cherry picked from commit a40235a2278a315261ee007fc433ec1cfb31666f) +--- + drivers/dma/pl330.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 16f578f1d050..642d07412d8d 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -2297,6 +2297,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + goto out; + + spin_lock_irqsave(&pch->lock, flags); ++ spin_lock(&pch->thread->dmac->lock); + + if (pch->thread->req_running != -1) + running = pch->thread->req[pch->thread->req_running].desc; +@@ -2339,6 +2340,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + if (desc->last) + residual = 0; + } ++ spin_unlock(&pch->thread->dmac->lock); + spin_unlock_irqrestore(&pch->lock, flags); + + out: + +From 58befcac6b343442beba9af186e5585c1a159288 Mon Sep 17 00:00:00 2001 +From: Stephen Barber +Date: Tue, 1 Nov 2016 16:44:27 -0700 +Subject: [PATCH 10/16] UPSTREAM: dmaengine: pl330: Handle xferred count if + DMAMOV hasn't finished + +After executing DMAGO it's possible that a request can come in for the +current xferred count, but if that happens too soon then DMAMOV SAR/DAR +may not have yet completed. If that happens, we should explicitly return 0 +since nothing has been transferred yet. + +Signed-off-by: Stephen Barber +Signed-off-by: Vinod Koul +(cherry picked from commit c44da03dd517c11c2b3525937b0a241fc1c69399) +--- + drivers/dma/pl330.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 642d07412d8d..700abe522316 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -2275,6 +2275,11 @@ static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, + } + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); + pm_runtime_put_autosuspend(pl330->ddma.dev); ++ ++ /* If DMAMOV hasn't finished yet, SAR/DAR can be zero */ ++ if (!val) ++ return 0; ++ + return val - addr; + } + + +From e169fa9c6afdc35362da8b6fee2822597400cb8c Mon Sep 17 00:00:00 2001 +From: Vladimir Murzin +Date: Wed, 7 Dec 2016 13:17:40 +0000 +Subject: [PATCH 11/16] UPSTREAM: dmaengine: pl330: do not generate unaligned + access + +When PL330 is used with !MMU the following fault is seen: + +Unhandled fault: alignment exception (0x801) at 0x8f26a002 +Internal error: : 801 [#1] ARM +Modules linked in: +CPU: 0 PID: 640 Comm: dma0chan0-copy0 Not tainted 4.8.0-6a82063-clean+ #1600 +Hardware name: ARM-Versatile Express +task: 8f1baa80 task.stack: 8e6fe000 +PC is at _setup_req+0x4c/0x350 +LR is at 0x8f2cbc00 +pc : [<801ea538>] lr : [<8f2cbc00>] psr: 60000093 +sp : 8e6ffdc0 ip : 00000000 fp : 00000000 +r10: 00000000 r9 : 8f2cba10 r8 : 8f2cbc00 +r7 : 80000013 r6 : 8f21a050 r5 : 8f21a000 r4 : 8f2ac800 +r3 : 8e6ffe18 r2 : 00944251 r1 : ffffffbc r0 : 8f26a000 +Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none +Control: 00c5387c +Process dma0chan0-copy0 (pid: 640, stack limit = 0x8e6fe210) +Stack: (0x8e6ffdc0 to 0x8e700000) +fdc0: 00000001 60000093 00000000 8f2cba10 8f26a000 00000004 8f0ae000 8f2cbc00 +fde0: 8f0ae000 8f2ac800 8f21a000 8f21a050 80000013 8f2cbc00 8f2cba10 00000000 +fe00: 60000093 801ebca0 8e6ffe18 000013ff 40000093 00000000 00944251 8f2ac800 +fe20: a0000013 8f2b1320 00001986 00000000 00000001 000013ff 8f1e4f00 8f2cba10 +fe40: 8e6fff6c 801e9044 00000003 00000000 fef98c80 002faf07 8e6ffe7c 00000000 +fe60: 00000002 00000000 00001986 8f1f158d 8f1e4f00 80568de4 00000002 00000000 +fe80: 00001986 8f1f53ff 40000001 80580500 8f1f158d 8001e00c 00000000 cfdfdfdf +fea0: fdae2a25 00000001 00000004 8e6fe000 00000008 00000010 00000000 00000005 +fec0: 8f2b1330 8f2b1334 8e6ffe80 8e6ffe8c 00001986 00000000 8f21a014 00000001 +fee0: 8e6ffe60 8e6ffe78 00000002 00000000 000013ff 00000001 80568de4 8f1e8018 +ff00: 0000158d 8055ec30 00000001 803f6b00 00001986 8f2cba10 fdae2a25 00000001 +ff20: 8f1baca8 8e6fff24 8e6fff24 00000000 8e6fff24 ac6f3037 00000000 00000000 +ff40: 00000000 8e6fe000 8f1e4f40 00000000 8f1e4f40 8f1e4f00 801e84ec 00000000 +ff60: 00000000 00000000 00000000 80031714 dfdfdfcf 00000000 dfdfdfcf 8f1e4f00 +ff80: 00000000 8e6fff84 8e6fff84 00000000 8e6fff90 8e6fff90 8e6fffac 8f1e4f40 +ffa0: 80031640 00000000 00000000 8000f548 00000000 00000000 00000000 00000000 +ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +ffe0: 00000000 00000000 00000000 00000000 00000013 00000000 dfdfdfcf cfdfdfdf +[<801ea538>] (_setup_req) from [<801ebca0>] (pl330_tasklet+0x41c/0x490) +[<801ebca0>] (pl330_tasklet) from [<801e9044>] (dmatest_func+0xb58/0x149c) +[<801e9044>] (dmatest_func) from [<80031714>] (kthread+0xd4/0xec) +[<80031714>] (kthread) from [<8000f548>] (ret_from_fork+0x14/0x2c) +Code: e3a03001 e3e01043 e5c03001 e59d3048 (e5802002) + +This happens because _emit_{ADDH,MOV,GO) accessing to unaligned data +while writing to buffer. Fix it with writing to buffer byte by byte. + +Reviewed-by: Robin Murphy +Tested-by: Robin Murphy +Signed-off-by: Vladimir Murzin +Signed-off-by: Vinod Koul +(cherry picked from commit d07c9e1e212c9687f9198bfeba582e86cae3f6f9) +--- + drivers/dma/pl330.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 700abe522316..c242c8634a58 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -573,7 +573,8 @@ static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[], + + buf[0] = CMD_DMAADDH; + buf[0] |= (da << 1); +- *((__le16 *)&buf[1]) = cpu_to_le16(val); ++ buf[1] = val; ++ buf[2] = val >> 8; + + PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n", + da == 1 ? "DA" : "SA", val); +@@ -727,7 +728,10 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], + + buf[0] = CMD_DMAMOV; + buf[1] = dst; +- *((__le32 *)&buf[2]) = cpu_to_le32(val); ++ buf[2] = val; ++ buf[3] = val >> 8; ++ buf[4] = val >> 16; ++ buf[5] = val >> 24; + + PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", + dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); +@@ -902,10 +906,11 @@ static inline u32 _emit_GO(unsigned dry_run, u8 buf[], + + buf[0] = CMD_DMAGO; + buf[0] |= (ns << 1); +- + buf[1] = chan & 0x7; +- +- *((__le32 *)&buf[2]) = cpu_to_le32(addr); ++ buf[2] = addr; ++ buf[3] = addr >> 8; ++ buf[4] = addr >> 16; ++ buf[5] = addr >> 24; + + return SZ_DMAGO; + } + +From 39f8d588ba2abc09f8232aab5de223eba7f2511c Mon Sep 17 00:00:00 2001 +From: Vinod Koul +Date: Fri, 9 Dec 2016 15:24:12 +0530 +Subject: [PATCH 12/16] =?UTF-8?q?UPSTREAM:=20dmaengine:=20pl330:=20remove?= + =?UTF-8?q?=20unused=20=E2=80=98regs=E2=80=99?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In pl330_add(), variable ‘regs’ is initialized but never used, which +leads to warning with W=1. + +drivers/dma/pl330.c: In function 'pl330_add': +drivers/dma/pl330.c:1891:16: warning: variable 'regs' set but not used [-Wunused-but-set-variable] + +So remove it. + +Cc: Linus Walleij +Signed-off-by: Vinod Koul +(cherry picked from commit 920e00d62ef9a818a4af7b2f9e1dbca23f846fc1) +--- + drivers/dma/pl330.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index c242c8634a58..da72d3d984e8 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1891,11 +1891,8 @@ static int dmac_alloc_resources(struct pl330_dmac *pl330) + + static int pl330_add(struct pl330_dmac *pl330) + { +- void __iomem *regs; + int i, ret; + +- regs = pl330->base; +- + /* Check if we can handle this DMAC */ + if ((pl330->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) { + dev_err(pl330->ddma.dev, "PERIPH_ID 0x%x !\n", + +From 8906839d1e7292db9f36ca135d0c640811ac08ee Mon Sep 17 00:00:00 2001 +From: Iago Abal +Date: Wed, 11 Jan 2017 14:00:21 +0100 +Subject: [PATCH 13/16] UPSTREAM: dmaengine: pl330: fix double lock + +The static bug finder EBA (http://www.iagoabal.eu/eba/) reported the +following double-lock bug: + + Double lock: + 1. spin_lock_irqsave(pch->lock, flags) at pl330_free_chan_resources:2236; + 2. call to function `pl330_release_channel' immediately after; + 3. call to function `dma_pl330_rqcb' in line 1753; + 4. spin_lock_irqsave(pch->lock, flags) at dma_pl330_rqcb:1505. + +I have fixed it as suggested by Marek Szyprowski. + +First, I have replaced `pch->lock' with `pl330->lock' in functions +`pl330_alloc_chan_resources' and `pl330_free_chan_resources'. This avoids +the double-lock by acquiring a different lock than `dma_pl330_rqcb'. + +NOTE that, as a result, `pl330_free_chan_resources' executes +`list_splice_tail_init' on `pch->work_list' under lock `pl330->lock', +whereas in the rest of the code `pch->work_list' is protected by +`pch->lock'. I don't know if this may cause race conditions. Similarly +`pch->cyclic' is written by `pl330_alloc_chan_resources' under +`pl330->lock' but read by `pl330_tx_submit' under `pch->lock'. + +Second, I have removed locking from `pl330_request_channel' and +`pl330_release_channel' functions. Function `pl330_request_channel' is +only called from `pl330_alloc_chan_resources', so the lock is already +held. Function `pl330_release_channel' is called from +`pl330_free_chan_resources', which already holds the lock, and from +`pl330_del'. Function `pl330_del' is called in an error path of +`pl330_probe' and at the end of `pl330_remove', but I assume that there +cannot be concurrent accesses to the protected data at those points. + +Signed-off-by: Iago Abal +Reviewed-by: Marek Szyprowski +Signed-off-by: Vinod Koul +(cherry picked from commit 91539eb1fda2d530d3b268eef542c5414e54bf1a) +--- + drivers/dma/pl330.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index da72d3d984e8..306186354e00 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1699,7 +1699,6 @@ static bool _chan_ns(const struct pl330_dmac *pl330, int i) + static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) + { + struct pl330_thread *thrd = NULL; +- unsigned long flags; + int chans, i; + + if (pl330->state == DYING) +@@ -1707,8 +1706,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) + + chans = pl330->pcfg.num_chan; + +- spin_lock_irqsave(&pl330->lock, flags); +- + for (i = 0; i < chans; i++) { + thrd = &pl330->channels[i]; + if ((thrd->free) && (!_manager_ns(thrd) || +@@ -1726,8 +1723,6 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) + thrd = NULL; + } + +- spin_unlock_irqrestore(&pl330->lock, flags); +- + return thrd; + } + +@@ -1745,7 +1740,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev) + static void pl330_release_channel(struct pl330_thread *thrd) + { + struct pl330_dmac *pl330; +- unsigned long flags; + + if (!thrd || thrd->free) + return; +@@ -1757,10 +1751,8 @@ static void pl330_release_channel(struct pl330_thread *thrd) + + pl330 = thrd->dmac; + +- spin_lock_irqsave(&pl330->lock, flags); + _free_event(thrd, thrd->ev); + thrd->free = true; +- spin_unlock_irqrestore(&pl330->lock, flags); + } + + /* Initialize the structure for PL330 configuration, that can be used +@@ -2124,20 +2116,20 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + struct pl330_dmac *pl330 = pch->dmac; + unsigned long flags; + +- spin_lock_irqsave(&pch->lock, flags); ++ spin_lock_irqsave(&pl330->lock, flags); + + dma_cookie_init(chan); + pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +- spin_unlock_irqrestore(&pch->lock, flags); ++ spin_unlock_irqrestore(&pl330->lock, flags); + return -ENOMEM; + } + + tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch); + +- spin_unlock_irqrestore(&pch->lock, flags); ++ spin_unlock_irqrestore(&pl330->lock, flags); + + return 1; + } +@@ -2240,12 +2232,13 @@ static int pl330_pause(struct dma_chan *chan) + static void pl330_free_chan_resources(struct dma_chan *chan) + { + struct dma_pl330_chan *pch = to_pchan(chan); ++ struct pl330_dmac *pl330 = pch->dmac; + unsigned long flags; + + tasklet_kill(&pch->task); + + pm_runtime_get_sync(pch->dmac->ddma.dev); +- spin_lock_irqsave(&pch->lock, flags); ++ spin_lock_irqsave(&pl330->lock, flags); + + pl330_release_channel(pch->thread); + pch->thread = NULL; +@@ -2253,7 +2246,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + if (pch->cyclic) + list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + +- spin_unlock_irqrestore(&pch->lock, flags); ++ spin_unlock_irqrestore(&pl330->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); + pm_runtime_put_autosuspend(pch->dmac->ddma.dev); + } + +From e6e9d20e0c601bc33868de2be0831d273851ddb6 Mon Sep 17 00:00:00 2001 +From: Jean-Philippe Brucker +Date: Thu, 1 Jun 2017 19:22:01 +0100 +Subject: [PATCH 14/16] UPSTREAM: dmaengine: pl330: fix warning in pl330_remove + +When removing a device with less than 9 IRQs (AMBA_NR_IRQS), we'll get a +big WARN_ON from devres.c because pl330_remove calls devm_free_irqs for +unallocated irqs. Similarly to pl330_probe, check that IRQ number is +present before calling devm_free_irq. + +Signed-off-by: Jean-Philippe Brucker +Signed-off-by: Vinod Koul +(cherry picked from commit ebcdaee4cebb3a8d0d702ab5e9392373672ec1de) +--- + drivers/dma/pl330.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 306186354e00..ddcb8029eddc 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -3038,7 +3038,8 @@ static int pl330_remove(struct amba_device *adev) + + for (i = 0; i < AMBA_NR_IRQS; i++) { + irq = adev->irq[i]; +- devm_free_irq(&adev->dev, irq, pl330); ++ if (irq) ++ devm_free_irq(&adev->dev, irq, pl330); + } + + dma_async_device_unregister(&pl330->ddma); + +From 59636648043a2ed182cf745956cf970702783ffe Mon Sep 17 00:00:00 2001 +From: Marek Szyprowski +Date: Mon, 27 Mar 2017 07:31:03 +0200 +Subject: [PATCH 15/16] UPSTREAM: dmaengine: pl330: remove pdata based + initialization + +This driver is now used only on platforms which support device tree, so +it is safe to remove legacy platform data based initialization code. + +Signed-off-by: Marek Szyprowski +Reviewed-by: Ulf Hansson +Acked-by: Arnd Bergmann +For plat-samsung: +Acked-by: Krzysztof Kozlowski +Signed-off-by: Vinod Koul +(cherry picked from commit e8bb4673596ea28fab287dbc417e8100d798cd40) +--- + arch/arm/plat-samsung/devs.c | 1 - + drivers/dma/pl330.c | 42 ++++++++---------------------------------- + include/linux/amba/pl330.h | 35 ----------------------------------- + 3 files changed, 8 insertions(+), 70 deletions(-) + delete mode 100644 include/linux/amba/pl330.h + +diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c +index e212f9d804bd..2ef19ad5cb62 100644 +--- a/arch/arm/plat-samsung/devs.c ++++ b/arch/arm/plat-samsung/devs.c +@@ -10,7 +10,6 @@ + * published by the Free Software Foundation. + */ + +-#include + #include + #include + #include +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index ddcb8029eddc..2044d36f81b1 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -22,7 +22,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -2078,18 +2077,6 @@ static void pl330_tasklet(unsigned long data) + } + } + +-bool pl330_filter(struct dma_chan *chan, void *param) +-{ +- u8 *peri_id; +- +- if (chan->device->dev->driver != &pl330_driver.drv) +- return false; +- +- peri_id = chan->private; +- return *peri_id == (unsigned long)param; +-} +-EXPORT_SYMBOL(pl330_filter); +- + static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) + { +@@ -2834,7 +2821,6 @@ static SIMPLE_DEV_PM_OPS(pl330_pm, pl330_suspend, pl330_resume); + static int + pl330_probe(struct amba_device *adev, const struct amba_id *id) + { +- struct dma_pl330_platdata *pdat; + struct pl330_config *pcfg; + struct pl330_dmac *pl330; + struct dma_pl330_chan *pch, *_p; +@@ -2844,8 +2830,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + int num_chan; + struct device_node *np = adev->dev.of_node; + +- pdat = dev_get_platdata(&adev->dev); +- + ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; +@@ -2860,7 +2844,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + pd = &pl330->ddma; + pd->dev = &adev->dev; + +- pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0; ++ pl330->mcbufsz = 0; + + /* get quirk */ + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) +@@ -2904,10 +2888,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + INIT_LIST_HEAD(&pd->channels); + + /* Initialize channel parameters */ +- if (pdat) +- num_chan = max_t(int, pdat->nr_valid_peri, pcfg->num_chan); +- else +- num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan); ++ num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan); + + pl330->num_peripherals = num_chan; + +@@ -2920,11 +2901,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + + for (i = 0; i < num_chan; i++) { + pch = &pl330->peripherals[i]; +- if (!adev->dev.of_node) +- pch->chan.private = pdat ? &pdat->peri_id[i] : NULL; +- else +- pch->chan.private = adev->dev.of_node; + ++ pch->chan.private = adev->dev.of_node; + INIT_LIST_HEAD(&pch->submitted_list); + INIT_LIST_HEAD(&pch->work_list); + INIT_LIST_HEAD(&pch->completed_list); +@@ -2937,15 +2915,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + list_add_tail(&pch->chan.device_node, &pd->channels); + } + +- if (pdat) { +- pd->cap_mask = pdat->cap_mask; +- } else { +- dma_cap_set(DMA_MEMCPY, pd->cap_mask); +- if (pcfg->num_peri) { +- dma_cap_set(DMA_SLAVE, pd->cap_mask); +- dma_cap_set(DMA_CYCLIC, pd->cap_mask); +- dma_cap_set(DMA_PRIVATE, pd->cap_mask); +- } ++ dma_cap_set(DMA_MEMCPY, pd->cap_mask); ++ if (pcfg->num_peri) { ++ dma_cap_set(DMA_SLAVE, pd->cap_mask); ++ dma_cap_set(DMA_CYCLIC, pd->cap_mask); ++ dma_cap_set(DMA_PRIVATE, pd->cap_mask); + } + + pd->device_alloc_chan_resources = pl330_alloc_chan_resources; +diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h +deleted file mode 100644 +index fe93758e8403..000000000000 +--- a/include/linux/amba/pl330.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-/* linux/include/linux/amba/pl330.h +- * +- * Copyright (C) 2010 Samsung Electronics Co. Ltd. +- * Jaswinder Singh +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- */ +- +-#ifndef __AMBA_PL330_H_ +-#define __AMBA_PL330_H_ +- +-#include +- +-struct dma_pl330_platdata { +- /* +- * Number of valid peripherals connected to DMAC. +- * This may be different from the value read from +- * CR0, as the PL330 implementation might have 'holes' +- * in the peri list or the peri could also be reached +- * from another DMAC which the platform prefers. +- */ +- u8 nr_valid_peri; +- /* Array of valid peripherals */ +- u8 *peri_id; +- /* Operational capabilities */ +- dma_cap_mask_t cap_mask; +- /* Bytes to allocate for MC buffer */ +- unsigned mcbuf_sz; +-}; +- +-extern bool pl330_filter(struct dma_chan *chan, void *param); +-#endif /* __AMBA_PL330_H_ */ + +From 163589ea5636b2eeb8d579251465ac1ce0fb0d8e Mon Sep 17 00:00:00 2001 +From: Matthias Kaehlcke +Date: Thu, 15 Jun 2017 16:55:57 -0700 +Subject: [PATCH 16/16] UPSTREAM: dmaengine: pl330: Delete unused functions + +The functions _queue_empty(), _emit_ADDH(), _emit_NOP(), _emit_STZ() +and _emit_WFE() are not used. Delete them. + +Signed-off-by: Matthias Kaehlcke +Signed-off-by: Vinod Koul +(cherry picked from commit d43674ecc002b49926f216cb414cff2d230ca3fb) +--- + drivers/dma/pl330.c | 67 ----------------------------------------------------- + 1 file changed, 67 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 2044d36f81b1..00ba5db20ec4 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -538,11 +538,6 @@ struct _xfer_spec { + struct dma_pl330_desc *desc; + }; + +-static inline bool _queue_empty(struct pl330_thread *thrd) +-{ +- return thrd->req[0].desc == NULL && thrd->req[1].desc == NULL; +-} +- + static inline bool _queue_full(struct pl330_thread *thrd) + { + return thrd->req[0].desc != NULL && thrd->req[1].desc != NULL; +@@ -564,23 +559,6 @@ static inline u32 get_revision(u32 periph_id) + return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK; + } + +-static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[], +- enum pl330_dst da, u16 val) +-{ +- if (dry_run) +- return SZ_DMAADDH; +- +- buf[0] = CMD_DMAADDH; +- buf[0] |= (da << 1); +- buf[1] = val; +- buf[2] = val >> 8; +- +- PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n", +- da == 1 ? "DA" : "SA", val); +- +- return SZ_DMAADDH; +-} +- + static inline u32 _emit_END(unsigned dry_run, u8 buf[]) + { + if (dry_run) +@@ -738,18 +716,6 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], + return SZ_DMAMOV; + } + +-static inline u32 _emit_NOP(unsigned dry_run, u8 buf[]) +-{ +- if (dry_run) +- return SZ_DMANOP; +- +- buf[0] = CMD_DMANOP; +- +- PL330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n"); +- +- return SZ_DMANOP; +-} +- + static inline u32 _emit_RMB(unsigned dry_run, u8 buf[]) + { + if (dry_run) +@@ -817,39 +783,6 @@ static inline u32 _emit_STP(unsigned dry_run, u8 buf[], + return SZ_DMASTP; + } + +-static inline u32 _emit_STZ(unsigned dry_run, u8 buf[]) +-{ +- if (dry_run) +- return SZ_DMASTZ; +- +- buf[0] = CMD_DMASTZ; +- +- PL330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n"); +- +- return SZ_DMASTZ; +-} +- +-static inline u32 _emit_WFE(unsigned dry_run, u8 buf[], u8 ev, +- unsigned invalidate) +-{ +- if (dry_run) +- return SZ_DMAWFE; +- +- buf[0] = CMD_DMAWFE; +- +- ev &= 0x1f; +- ev <<= 3; +- buf[1] = ev; +- +- if (invalidate) +- buf[1] |= (1 << 1); +- +- PL330_DBGCMD_DUMP(SZ_DMAWFE, "\tDMAWFE %u%s\n", +- ev >> 3, invalidate ? ", I" : ""); +- +- return SZ_DMAWFE; +-} +- + static inline u32 _emit_WFP(unsigned dry_run, u8 buf[], + enum pl330_cond cond, u8 peri) + {