From 40214ec132c092dcd75a8ab0d27ccc1d0e1e2827 Mon Sep 17 00:00:00 2001 From: Paolo Sabatino Date: Sun, 26 Jan 2025 14:52:54 +0100 Subject: [PATCH] rockchip/64: improvements to pl330 dma driver --- .../archive/rockchip-6.12/armbian.series | 5 + .../general-increase-spdif-dma-burst.patch | 25 ++ ...eral-pl330-01-fix-periodic-transfers.patch | 421 ++++++++++++++++++ ...dd-support-for-interleaved-transfers.patch | 261 +++++++++++ ...330-03-fix-data-race-on-tx-transfers.patch | 47 ++ ...general-pl330-04-bigger-mcode-buffer.patch | 27 ++ .../kernel/archive/rockchip-6.12/series.conf | 5 + .../archive/rockchip-6.13/armbian.series | 5 + .../general-increase-spdif-dma-burst.patch | 25 ++ ...eral-pl330-01-fix-periodic-transfers.patch | 421 ++++++++++++++++++ ...dd-support-for-interleaved-transfers.patch | 261 +++++++++++ ...330-03-fix-data-race-on-tx-transfers.patch | 47 ++ ...general-pl330-04-bigger-mcode-buffer.patch | 27 ++ .../kernel/archive/rockchip-6.13/series.conf | 5 + .../general-increase-spdif-dma-burst.patch | 25 ++ ...eral-pl330-01-fix-periodic-transfers.patch | 421 ++++++++++++++++++ ...dd-support-for-interleaved-transfers.patch | 261 +++++++++++ ...330-03-fix-data-race-on-tx-transfers.patch | 47 ++ ...general-pl330-04-bigger-mcode-buffer.patch | 27 ++ .../general-increase-spdif-dma-burst.patch | 25 ++ ...eral-pl330-01-fix-periodic-transfers.patch | 421 ++++++++++++++++++ ...dd-support-for-interleaved-transfers.patch | 261 +++++++++++ ...330-03-fix-data-race-on-tx-transfers.patch | 47 ++ ...general-pl330-04-bigger-mcode-buffer.patch | 27 ++ 24 files changed, 3144 insertions(+) create mode 100644 patch/kernel/archive/rockchip-6.12/patches.armbian/general-increase-spdif-dma-burst.patch create mode 100644 patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-01-fix-periodic-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch create mode 100644 patch/kernel/archive/rockchip-6.13/patches.armbian/general-increase-spdif-dma-burst.patch create mode 100644 patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-01-fix-periodic-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch create mode 100644 patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch create mode 100644 patch/kernel/archive/rockchip64-6.12/general-increase-spdif-dma-burst.patch create mode 100644 patch/kernel/archive/rockchip64-6.12/general-pl330-01-fix-periodic-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.12/general-pl330-02-add-support-for-interleaved-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.12/general-pl330-03-fix-data-race-on-tx-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.12/general-pl330-04-bigger-mcode-buffer.patch create mode 100644 patch/kernel/archive/rockchip64-6.13/general-increase-spdif-dma-burst.patch create mode 100644 patch/kernel/archive/rockchip64-6.13/general-pl330-01-fix-periodic-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.13/general-pl330-02-add-support-for-interleaved-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.13/general-pl330-03-fix-data-race-on-tx-transfers.patch create mode 100644 patch/kernel/archive/rockchip64-6.13/general-pl330-04-bigger-mcode-buffer.patch diff --git a/patch/kernel/archive/rockchip-6.12/armbian.series b/patch/kernel/archive/rockchip-6.12/armbian.series index 847a170939ac..2b7e00acee19 100644 --- a/patch/kernel/archive/rockchip-6.12/armbian.series +++ b/patch/kernel/archive/rockchip-6.12/armbian.series @@ -34,7 +34,12 @@ patches.armbian/general-dwc2-fix-wait-time.patch patches.armbian/general-dwc2-nak-gadget.patch patches.armbian/general-fix-reboot-from-kwiboo.patch + patches.armbian/general-increase-spdif-dma-burst.patch patches.armbian/general-linux-export-mm-trace-rss-stats.patch + patches.armbian/general-pl330-01-fix-periodic-transfers.patch + patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch + patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch + patches.armbian/general-pl330-04-bigger-mcode-buffer.patch patches.armbian/general-rk322x-gpio-ir-driver.patch patches.armbian/general-rockchip-various-fixes.patch patches.armbian/ir-keymap-rk322x-box.patch diff --git a/patch/kernel/archive/rockchip-6.12/patches.armbian/general-increase-spdif-dma-burst.patch b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-increase-spdif-dma-burst.patch new file mode 100644 index 000000000000..9d3a91b4dd94 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-increase-spdif-dma-burst.patch @@ -0,0 +1,25 @@ +From 379651eb82cf5966a40a5b931afc2fa91c6a311d Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:39:03 +0100 +Subject: [PATCH 2/2] rockchip: increase SPDIF max burst value to maximum + +--- + sound/soc/rockchip/rockchip_spdif.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +index d87c0e4f6f91..7a2cfecf6a94 100644 +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -329,7 +329,7 @@ static int rk_spdif_probe(struct platform_device *pdev) + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- spdif->playback_dma_data.maxburst = 4; ++ spdif->playback_dma_data.maxburst = 8; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-01-fix-periodic-transfers.patch b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-01-fix-periodic-transfers.patch new file mode 100644 index 000000000000..844286d43db2 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-01-fix-periodic-transfers.patch @@ -0,0 +1,421 @@ +From fc0d09bf651fcab0998da4d187a91f64df419188 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:36:50 +0100 +Subject: [PATCH 1/2] pl330: fix dma engine periodic transfers + +--- + drivers/dma/pl330.c | 277 +++++++++++++++++++++++++++++--------------- + 1 file changed, 186 insertions(+), 91 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 82a9fe88ad54..ef197c4cfed4 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -239,6 +239,7 @@ 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 +@@ -455,9 +456,6 @@ struct dma_pl330_chan { + enum dma_data_direction dir; + struct dma_slave_config slave_config; + +- /* for cyclic capability */ +- bool cyclic; +- + /* for runtime pm tracking */ + bool active; + }; +@@ -545,6 +543,10 @@ 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 { +@@ -1368,6 +1370,108 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], + return off; + } + ++static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], ++ unsigned long bursts, const struct _xfer_spec *pxs, int ev) ++{ ++ unsigned int lcnt1, ljmp1; ++ int cyc, off = 0, num_dregs = 0; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ if (bursts > 256) { ++ lcnt1 = 256; ++ cyc = bursts / 256; ++ } else { ++ lcnt1 = bursts; ++ cyc = 1; ++ } ++ ++ /* 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); ++ } ++ ++ num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); ++ ++ if (num_dregs) { ++ off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); ++ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); ++ } ++ ++ off += _emit_SEV(dry_run, &buf[off], ev); ++ ++ return off; ++} ++ ++static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned int dry_run, ++ u8 buf[], unsigned long bursts, ++ const struct _xfer_spec *pxs, int ev) ++{ ++ int off, periods, residue, i; ++ unsigned int lcnt0, ljmp0, ljmpfe; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ off = 0; ++ ljmpfe = off; ++ lcnt0 = pxs->desc->num_periods; ++ periods = 1; ++ ++ while (lcnt0 > 256) { ++ periods++; ++ lcnt0 = pxs->desc->num_periods / periods; ++ } ++ ++ residue = pxs->desc->num_periods % periods; ++ ++ /* 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; ++ ++ for (i = 0; i < periods; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ lpend.cond = ALWAYS; ++ lpend.forever = false; ++ lpend.loop = 0; ++ lpend.bjump = off - ljmp0; ++ off += _emit_LPEND(dry_run, &buf[off], &lpend); ++ ++ for (i = 0; i < residue; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ 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) +@@ -1407,6 +1511,21 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + return off; + } + ++static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, ++ unsigned int 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. +@@ -1424,12 +1543,17 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + +- off += _setup_xfer(pl330, dry_run, &buf[off], pxs); ++ if (!pxs->desc->cyclic) { ++ off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + +- /* DMASEV peripheral/event */ +- off += _emit_SEV(dry_run, &buf[off], thrd->ev); +- /* DMAEND */ +- off += _emit_END(dry_run, &buf[off]); ++ /* DMASEV peripheral/event */ ++ off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ /* DMAEND */ ++ off += _emit_END(dry_run, &buf[off]); ++ } else { ++ off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], ++ pxs, thrd->ev); ++ } + + return off; + } +@@ -1703,15 +1827,17 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; +- thrd->req[active].desc = NULL; +- +- thrd->req_running = -1; +- +- /* Get going again ASAP */ +- pl330_start_thread(thrd); +- +- /* For now, just make a list of callbacks to be done */ +- list_add_tail(&descdone->rqd, &pl330->req_done); ++ if (descdone) { ++ if (!descdone->cyclic) { ++ thrd->req[active].desc = NULL; ++ thrd->req_running = -1; ++ /* Get going again ASAP */ ++ pl330_start_thread(thrd); ++ } ++ ++ /* For now, just make a list of callbacks to be done */ ++ list_add_tail(&descdone->rqd, &pl330->req_done); ++ } + } + } + +@@ -2076,12 +2202,25 @@ static void pl330_tasklet(struct tasklet_struct *t) + 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 (!pch->cyclic) ++ if (!desc->cyclic) { + dma_cookie_complete(&desc->txd); +- list_move_tail(&desc->node, &pch->completed_list); ++ list_move_tail(&desc->node, &pch->completed_list); ++ } else { ++ struct dmaengine_desc_callback cb; ++ ++ desc->status = BUSY; ++ dmaengine_desc_get_callback(&desc->txd, &cb); ++ ++ if (dmaengine_desc_callback_valid(&cb)) { ++ spin_unlock_irqrestore(&pch->lock, flags); ++ dmaengine_desc_callback_invoke(&cb, NULL); ++ spin_lock_irqsave(&pch->lock, flags); ++ } ++ } + } ++ } + + /* Try to submit a req imm. next to the last completed cookie */ + fill_queue(pch); +@@ -2107,20 +2246,8 @@ static void pl330_tasklet(struct tasklet_struct *t) + + dmaengine_desc_get_callback(&desc->txd, &cb); + +- 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); +- pl330_start_thread(pch->thread); +- spin_unlock(&pch->thread->dmac->lock); +- power_down = false; +- } +- } else { +- desc->status = FREE; +- list_move_tail(&desc->node, &pch->dmac->desc_pool); +- } ++ desc->status = FREE; ++ list_move_tail(&desc->node, &pch->dmac->desc_pool); + + dma_descriptor_unmap(&desc->txd); + +@@ -2168,7 +2295,6 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + spin_lock_irqsave(&pl330->lock, flags); + + dma_cookie_init(chan); +- pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +@@ -2367,8 +2493,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + pl330_release_channel(pch->thread); + pch->thread = NULL; + +- if (pch->cyclic) +- list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); ++ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + + spin_unlock_irqrestore(&pl330->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); +@@ -2431,7 +2556,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) ++ if (desc->status == DONE && !desc->cyclic) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = +@@ -2516,10 +2641,7 @@ 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); +@@ -2622,6 +2744,9 @@ 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; +@@ -2685,12 +2810,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, *first = NULL; ++ struct dma_pl330_desc *desc = 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; ++ dma_addr_t dst = 0; ++ dma_addr_t src = 0; + + if (len % period_len != 0) + return NULL; +@@ -2706,33 +2829,14 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + if (!pl330_prep_slave_fifo(pch, direction)) + return NULL; + +- for (i = 0; i < len / period_len; i++) { +- desc = pl330_get_desc(pch); +- if (!desc) { +- unsigned long iflags; +- +- dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", +- __func__, __LINE__); +- +- if (!first) +- return NULL; +- +- spin_lock_irqsave(&pl330->pool_lock, iflags); +- +- 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, iflags); +- +- 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; ++ } + +- switch (direction) { ++ switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; +@@ -2746,27 +2850,18 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + dst = dma_addr; + break; + default: +- break; +- } +- +- desc->rqtype = direction; +- desc->rqcfg.brst_size = pch->burst_sz; +- desc->rqcfg.brst_len = pch->burst_len; +- 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; ++ break; + } + +- if (!desc) +- return NULL; ++ 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); + +- pch->cyclic = true; ++ desc->cyclic = true; ++ desc->num_periods = len / period_len; ++ desc->txd.flags = flags; + + return &desc->txd; + } +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch new file mode 100644 index 000000000000..54f0dd9134f2 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch @@ -0,0 +1,261 @@ +From e691c5c3feede95b4e159344aaea070fc428c847 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:23:50 +0100 +Subject: [PATCH 1/2] rockchip/64: pl330 - add support for interleaved + transfers + +original source: https://patchwork.kernel.org/project/linux-rockchip/cover/1712150304-60832-1-git-send-email-sugar.zhang@rock-chips.com/ +--- + drivers/dma/pl330.c | 168 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 162 insertions(+), 6 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index ef197c4cfed4..b49a3a6c4686 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -543,6 +543,8 @@ struct dma_pl330_desc { + unsigned peri:5; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; ++ /* interleaved size */ ++ struct data_chunk sgl; + + /* For cyclic capability */ + bool cyclic; +@@ -579,6 +581,22 @@ 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); ++ *((__le16 *)&buf[1]) = cpu_to_le16(val); ++ ++ 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) +@@ -1189,7 +1207,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + const struct _xfer_spec *pxs, int cyc, + enum pl330_cond cond) + { +- int off = 0; ++ int off = 0, i = 0, burstn = 1; + + /* + * do FLUSHP at beginning to clear any stale dma requests before the +@@ -1197,12 +1215,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + */ + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); ++ ++ if (pxs->desc->sgl.size) { ++ WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); ++ burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); ++ } ++ + while (cyc--) { +- off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +- off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); +- off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); ++ for (i = 0; i < burstn; i++) { ++ off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); ++ off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ } ++ ++ switch (pxs->desc->rqtype) { ++ case DMA_DEV_TO_MEM: ++ if (pxs->desc->sgl.dst_icg) ++ off += _emit_ADDH(dry_run, &buf[off], DST, ++ pxs->desc->sgl.dst_icg); ++ break; ++ case DMA_MEM_TO_DEV: ++ if (pxs->desc->sgl.src_icg) ++ off += _emit_ADDH(dry_run, &buf[off], SRC, ++ pxs->desc->sgl.src_icg); ++ break; ++ default: ++ WARN_ON(1); ++ break; ++ } + } + + return off; +@@ -1483,6 +1525,9 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + BRST_SIZE(ccr); + int off = 0; + ++ if (pxs->desc->sgl.size) ++ bursts = x->bytes / pxs->desc->sgl.size; ++ + while (bursts) { + c = bursts; + off += _loop(pl330, dry_run, &buf[off], &c, pxs); +@@ -2743,6 +2788,9 @@ 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->sgl.size = 0; ++ desc->sgl.src_icg = 0; ++ desc->sgl.dst_icg = 0; + + desc->cyclic = false; + desc->num_periods = 1; +@@ -2866,6 +2914,110 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + return &desc->txd; + } + ++static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( ++ struct dma_chan *chan, struct dma_interleaved_template *xt, ++ unsigned long flags) ++{ ++ 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; ++ size_t size, src_icg, dst_icg, period_bytes, buffer_bytes, full_period_bytes; ++ size_t nump = 0, numf = 0; ++ ++ if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) ++ return NULL; ++ nump = xt->nump; ++ numf = xt->numf; ++ size = xt->sgl[0].size; ++ period_bytes = size * nump; ++ buffer_bytes = size * numf; ++ ++ if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) ++ return NULL; ++ ++ src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); ++ dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); ++ ++ pl330_config_write(chan, &pch->slave_config, xt->dir); ++ ++ if (!pl330_prep_slave_fifo(pch, xt->dir)) ++ return NULL; ++ ++ for (i = 0; i < numf / nump; i++) { ++ desc = pl330_get_desc(pch); ++ if (!desc) { ++ unsigned long iflags; ++ ++ dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", ++ __func__, __LINE__); ++ ++ if (!first) ++ return NULL; ++ ++ spin_lock_irqsave(&pl330->pool_lock, iflags); ++ ++ 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, iflags); ++ ++ return NULL; ++ } ++ ++ switch (xt->dir) { ++ case DMA_MEM_TO_DEV: ++ desc->rqcfg.src_inc = 1; ++ desc->rqcfg.dst_inc = 0; ++ src = xt->src_start + period_bytes * i; ++ dst = pch->fifo_dma; ++ full_period_bytes = (size + src_icg) * nump; ++ break; ++ case DMA_DEV_TO_MEM: ++ desc->rqcfg.src_inc = 0; ++ desc->rqcfg.dst_inc = 1; ++ src = pch->fifo_dma; ++ dst = xt->dst_start + period_bytes * i; ++ full_period_bytes = (size + dst_icg) * nump; ++ break; ++ default: ++ break; ++ } ++ ++ desc->rqtype = xt->dir; ++ desc->rqcfg.brst_size = pch->burst_sz; ++ desc->rqcfg.brst_len = pch->burst_len; ++ desc->bytes_requested = full_period_bytes; ++ desc->sgl.size = size; ++ desc->sgl.src_icg = src_icg; ++ desc->sgl.dst_icg = dst_icg; ++ fill_px(&desc->px, dst, src, period_bytes); ++ ++ if (!first) ++ first = desc; ++ else ++ list_add_tail(&desc->node, &first->node); ++ } ++ ++ if (!desc) ++ return NULL; ++ ++ if (flags & DMA_PREP_REPEAT) ++ desc->cyclic = true; ++ ++ dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", ++ size, src_icg, dst_icg, nump, numf); ++ ++ return &desc->txd; ++} ++ + static struct dma_async_tx_descriptor * + pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +@@ -3221,12 +3373,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + 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_INTERLEAVE, pd->cap_mask); ++ dma_cap_set(DMA_REPEAT, pd->cap_mask); ++ dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); + } + + 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_cyclic = pl330_prep_dma_cyclic; ++ pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; + pd->device_tx_status = pl330_tx_status; + pd->device_prep_slave_sg = pl330_prep_slave_sg; + pd->device_config = pl330_config; +diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h +index b137fdb56093..63624c5836cf 100644 +--- a/include/linux/dmaengine.h ++++ b/include/linux/dmaengine.h +@@ -156,6 +156,7 @@ struct dma_interleaved_template { + bool src_sgl; + bool dst_sgl; + size_t numf; ++ size_t nump; + size_t frame_size; + struct data_chunk sgl[]; + }; +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch new file mode 100644 index 000000000000..9caad0107c4f --- /dev/null +++ b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch @@ -0,0 +1,47 @@ +From 5654890f22bc9f08ac2afd051c935ba8f8bc7e33 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:26:49 +0100 +Subject: [PATCH 2/2] rockchip/64: pl330: Fix data race on TX transferred bytes + reporting + +original source: https://patchwork.kernel.org/project/linux-rockchip/patch/20170302125710.14483-1-romain.perier@collabora.com/ +--- + drivers/dma/pl330.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index b49a3a6c4686..62003b67c231 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1872,6 +1872,7 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; ++ descdone->status = DONE; + if (descdone) { + if (!descdone->cyclic) { + thrd->req[active].desc = NULL; +@@ -1887,12 +1888,19 @@ static int pl330_update(struct pl330_dmac *pl330) + } + + /* Now that we are in no hurry, do the callbacks */ ++ struct dma_pl330_chan *pch; + while (!list_empty(&pl330->req_done)) { + descdone = list_first_entry(&pl330->req_done, + struct dma_pl330_desc, rqd); + list_del(&descdone->rqd); + spin_unlock_irqrestore(&pl330->lock, flags); +- dma_pl330_rqcb(descdone, PL330_ERR_NONE); ++ pch = descdone->pchan; ++ /* If desc aborted */ ++ if (!pch) { ++ spin_lock_irqsave(&pl330->lock, flags); ++ continue; ++ } ++ tasklet_schedule(&pch->task); + spin_lock_irqsave(&pl330->lock, flags); + } + +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch new file mode 100644 index 000000000000..15b348888936 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.12/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch @@ -0,0 +1,27 @@ +From f695d6bfdd099ec6f59a73aa792b97df80c72d54 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 26 Jan 2025 14:49:18 +0100 +Subject: [PATCH] increase pl330 microcode buffer size + +suggestion comes from the scatter/gather functionality as +proposed here: https://github.com/radxa/kernel/commit/ec0b65dbc59793426b6dc7af06ab6675f4a24940 +--- + 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 82a9fe88ad54..eb322c7ae5de 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -246,7 +246,7 @@ enum pl330_byteswap { + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +-#define MCODE_BUFF_PER_REQ 256 ++#define MCODE_BUFF_PER_REQ 512 + + /* Use this _only_ to wait on transient states */ + #define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.12/series.conf b/patch/kernel/archive/rockchip-6.12/series.conf index 9d32a6f1fe3c..1a263bf68780 100644 --- a/patch/kernel/archive/rockchip-6.12/series.conf +++ b/patch/kernel/archive/rockchip-6.12/series.conf @@ -42,7 +42,12 @@ patches.armbian/general-dwc2-fix-wait-time.patch patches.armbian/general-dwc2-nak-gadget.patch patches.armbian/general-fix-reboot-from-kwiboo.patch + patches.armbian/general-increase-spdif-dma-burst.patch patches.armbian/general-linux-export-mm-trace-rss-stats.patch + patches.armbian/general-pl330-01-fix-periodic-transfers.patch + patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch + patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch + patches.armbian/general-pl330-04-bigger-mcode-buffer.patch patches.armbian/general-rk322x-gpio-ir-driver.patch patches.armbian/general-rockchip-various-fixes.patch patches.armbian/ir-keymap-rk322x-box.patch diff --git a/patch/kernel/archive/rockchip-6.13/armbian.series b/patch/kernel/archive/rockchip-6.13/armbian.series index 847a170939ac..2b7e00acee19 100644 --- a/patch/kernel/archive/rockchip-6.13/armbian.series +++ b/patch/kernel/archive/rockchip-6.13/armbian.series @@ -34,7 +34,12 @@ patches.armbian/general-dwc2-fix-wait-time.patch patches.armbian/general-dwc2-nak-gadget.patch patches.armbian/general-fix-reboot-from-kwiboo.patch + patches.armbian/general-increase-spdif-dma-burst.patch patches.armbian/general-linux-export-mm-trace-rss-stats.patch + patches.armbian/general-pl330-01-fix-periodic-transfers.patch + patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch + patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch + patches.armbian/general-pl330-04-bigger-mcode-buffer.patch patches.armbian/general-rk322x-gpio-ir-driver.patch patches.armbian/general-rockchip-various-fixes.patch patches.armbian/ir-keymap-rk322x-box.patch diff --git a/patch/kernel/archive/rockchip-6.13/patches.armbian/general-increase-spdif-dma-burst.patch b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-increase-spdif-dma-burst.patch new file mode 100644 index 000000000000..9d3a91b4dd94 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-increase-spdif-dma-burst.patch @@ -0,0 +1,25 @@ +From 379651eb82cf5966a40a5b931afc2fa91c6a311d Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:39:03 +0100 +Subject: [PATCH 2/2] rockchip: increase SPDIF max burst value to maximum + +--- + sound/soc/rockchip/rockchip_spdif.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +index d87c0e4f6f91..7a2cfecf6a94 100644 +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -329,7 +329,7 @@ static int rk_spdif_probe(struct platform_device *pdev) + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- spdif->playback_dma_data.maxburst = 4; ++ spdif->playback_dma_data.maxburst = 8; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-01-fix-periodic-transfers.patch b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-01-fix-periodic-transfers.patch new file mode 100644 index 000000000000..844286d43db2 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-01-fix-periodic-transfers.patch @@ -0,0 +1,421 @@ +From fc0d09bf651fcab0998da4d187a91f64df419188 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:36:50 +0100 +Subject: [PATCH 1/2] pl330: fix dma engine periodic transfers + +--- + drivers/dma/pl330.c | 277 +++++++++++++++++++++++++++++--------------- + 1 file changed, 186 insertions(+), 91 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 82a9fe88ad54..ef197c4cfed4 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -239,6 +239,7 @@ 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 +@@ -455,9 +456,6 @@ struct dma_pl330_chan { + enum dma_data_direction dir; + struct dma_slave_config slave_config; + +- /* for cyclic capability */ +- bool cyclic; +- + /* for runtime pm tracking */ + bool active; + }; +@@ -545,6 +543,10 @@ 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 { +@@ -1368,6 +1370,108 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], + return off; + } + ++static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], ++ unsigned long bursts, const struct _xfer_spec *pxs, int ev) ++{ ++ unsigned int lcnt1, ljmp1; ++ int cyc, off = 0, num_dregs = 0; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ if (bursts > 256) { ++ lcnt1 = 256; ++ cyc = bursts / 256; ++ } else { ++ lcnt1 = bursts; ++ cyc = 1; ++ } ++ ++ /* 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); ++ } ++ ++ num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); ++ ++ if (num_dregs) { ++ off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); ++ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); ++ } ++ ++ off += _emit_SEV(dry_run, &buf[off], ev); ++ ++ return off; ++} ++ ++static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned int dry_run, ++ u8 buf[], unsigned long bursts, ++ const struct _xfer_spec *pxs, int ev) ++{ ++ int off, periods, residue, i; ++ unsigned int lcnt0, ljmp0, ljmpfe; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ off = 0; ++ ljmpfe = off; ++ lcnt0 = pxs->desc->num_periods; ++ periods = 1; ++ ++ while (lcnt0 > 256) { ++ periods++; ++ lcnt0 = pxs->desc->num_periods / periods; ++ } ++ ++ residue = pxs->desc->num_periods % periods; ++ ++ /* 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; ++ ++ for (i = 0; i < periods; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ lpend.cond = ALWAYS; ++ lpend.forever = false; ++ lpend.loop = 0; ++ lpend.bjump = off - ljmp0; ++ off += _emit_LPEND(dry_run, &buf[off], &lpend); ++ ++ for (i = 0; i < residue; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ 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) +@@ -1407,6 +1511,21 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + return off; + } + ++static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, ++ unsigned int 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. +@@ -1424,12 +1543,17 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + +- off += _setup_xfer(pl330, dry_run, &buf[off], pxs); ++ if (!pxs->desc->cyclic) { ++ off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + +- /* DMASEV peripheral/event */ +- off += _emit_SEV(dry_run, &buf[off], thrd->ev); +- /* DMAEND */ +- off += _emit_END(dry_run, &buf[off]); ++ /* DMASEV peripheral/event */ ++ off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ /* DMAEND */ ++ off += _emit_END(dry_run, &buf[off]); ++ } else { ++ off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], ++ pxs, thrd->ev); ++ } + + return off; + } +@@ -1703,15 +1827,17 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; +- thrd->req[active].desc = NULL; +- +- thrd->req_running = -1; +- +- /* Get going again ASAP */ +- pl330_start_thread(thrd); +- +- /* For now, just make a list of callbacks to be done */ +- list_add_tail(&descdone->rqd, &pl330->req_done); ++ if (descdone) { ++ if (!descdone->cyclic) { ++ thrd->req[active].desc = NULL; ++ thrd->req_running = -1; ++ /* Get going again ASAP */ ++ pl330_start_thread(thrd); ++ } ++ ++ /* For now, just make a list of callbacks to be done */ ++ list_add_tail(&descdone->rqd, &pl330->req_done); ++ } + } + } + +@@ -2076,12 +2202,25 @@ static void pl330_tasklet(struct tasklet_struct *t) + 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 (!pch->cyclic) ++ if (!desc->cyclic) { + dma_cookie_complete(&desc->txd); +- list_move_tail(&desc->node, &pch->completed_list); ++ list_move_tail(&desc->node, &pch->completed_list); ++ } else { ++ struct dmaengine_desc_callback cb; ++ ++ desc->status = BUSY; ++ dmaengine_desc_get_callback(&desc->txd, &cb); ++ ++ if (dmaengine_desc_callback_valid(&cb)) { ++ spin_unlock_irqrestore(&pch->lock, flags); ++ dmaengine_desc_callback_invoke(&cb, NULL); ++ spin_lock_irqsave(&pch->lock, flags); ++ } ++ } + } ++ } + + /* Try to submit a req imm. next to the last completed cookie */ + fill_queue(pch); +@@ -2107,20 +2246,8 @@ static void pl330_tasklet(struct tasklet_struct *t) + + dmaengine_desc_get_callback(&desc->txd, &cb); + +- 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); +- pl330_start_thread(pch->thread); +- spin_unlock(&pch->thread->dmac->lock); +- power_down = false; +- } +- } else { +- desc->status = FREE; +- list_move_tail(&desc->node, &pch->dmac->desc_pool); +- } ++ desc->status = FREE; ++ list_move_tail(&desc->node, &pch->dmac->desc_pool); + + dma_descriptor_unmap(&desc->txd); + +@@ -2168,7 +2295,6 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + spin_lock_irqsave(&pl330->lock, flags); + + dma_cookie_init(chan); +- pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +@@ -2367,8 +2493,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + pl330_release_channel(pch->thread); + pch->thread = NULL; + +- if (pch->cyclic) +- list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); ++ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + + spin_unlock_irqrestore(&pl330->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); +@@ -2431,7 +2556,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) ++ if (desc->status == DONE && !desc->cyclic) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = +@@ -2516,10 +2641,7 @@ 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); +@@ -2622,6 +2744,9 @@ 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; +@@ -2685,12 +2810,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, *first = NULL; ++ struct dma_pl330_desc *desc = 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; ++ dma_addr_t dst = 0; ++ dma_addr_t src = 0; + + if (len % period_len != 0) + return NULL; +@@ -2706,33 +2829,14 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + if (!pl330_prep_slave_fifo(pch, direction)) + return NULL; + +- for (i = 0; i < len / period_len; i++) { +- desc = pl330_get_desc(pch); +- if (!desc) { +- unsigned long iflags; +- +- dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", +- __func__, __LINE__); +- +- if (!first) +- return NULL; +- +- spin_lock_irqsave(&pl330->pool_lock, iflags); +- +- 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, iflags); +- +- 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; ++ } + +- switch (direction) { ++ switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; +@@ -2746,27 +2850,18 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + dst = dma_addr; + break; + default: +- break; +- } +- +- desc->rqtype = direction; +- desc->rqcfg.brst_size = pch->burst_sz; +- desc->rqcfg.brst_len = pch->burst_len; +- 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; ++ break; + } + +- if (!desc) +- return NULL; ++ 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); + +- pch->cyclic = true; ++ desc->cyclic = true; ++ desc->num_periods = len / period_len; ++ desc->txd.flags = flags; + + return &desc->txd; + } +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch new file mode 100644 index 000000000000..54f0dd9134f2 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch @@ -0,0 +1,261 @@ +From e691c5c3feede95b4e159344aaea070fc428c847 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:23:50 +0100 +Subject: [PATCH 1/2] rockchip/64: pl330 - add support for interleaved + transfers + +original source: https://patchwork.kernel.org/project/linux-rockchip/cover/1712150304-60832-1-git-send-email-sugar.zhang@rock-chips.com/ +--- + drivers/dma/pl330.c | 168 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 162 insertions(+), 6 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index ef197c4cfed4..b49a3a6c4686 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -543,6 +543,8 @@ struct dma_pl330_desc { + unsigned peri:5; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; ++ /* interleaved size */ ++ struct data_chunk sgl; + + /* For cyclic capability */ + bool cyclic; +@@ -579,6 +581,22 @@ 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); ++ *((__le16 *)&buf[1]) = cpu_to_le16(val); ++ ++ 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) +@@ -1189,7 +1207,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + const struct _xfer_spec *pxs, int cyc, + enum pl330_cond cond) + { +- int off = 0; ++ int off = 0, i = 0, burstn = 1; + + /* + * do FLUSHP at beginning to clear any stale dma requests before the +@@ -1197,12 +1215,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + */ + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); ++ ++ if (pxs->desc->sgl.size) { ++ WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); ++ burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); ++ } ++ + while (cyc--) { +- off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +- off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); +- off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); ++ for (i = 0; i < burstn; i++) { ++ off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); ++ off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ } ++ ++ switch (pxs->desc->rqtype) { ++ case DMA_DEV_TO_MEM: ++ if (pxs->desc->sgl.dst_icg) ++ off += _emit_ADDH(dry_run, &buf[off], DST, ++ pxs->desc->sgl.dst_icg); ++ break; ++ case DMA_MEM_TO_DEV: ++ if (pxs->desc->sgl.src_icg) ++ off += _emit_ADDH(dry_run, &buf[off], SRC, ++ pxs->desc->sgl.src_icg); ++ break; ++ default: ++ WARN_ON(1); ++ break; ++ } + } + + return off; +@@ -1483,6 +1525,9 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + BRST_SIZE(ccr); + int off = 0; + ++ if (pxs->desc->sgl.size) ++ bursts = x->bytes / pxs->desc->sgl.size; ++ + while (bursts) { + c = bursts; + off += _loop(pl330, dry_run, &buf[off], &c, pxs); +@@ -2743,6 +2788,9 @@ 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->sgl.size = 0; ++ desc->sgl.src_icg = 0; ++ desc->sgl.dst_icg = 0; + + desc->cyclic = false; + desc->num_periods = 1; +@@ -2866,6 +2914,110 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + return &desc->txd; + } + ++static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( ++ struct dma_chan *chan, struct dma_interleaved_template *xt, ++ unsigned long flags) ++{ ++ 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; ++ size_t size, src_icg, dst_icg, period_bytes, buffer_bytes, full_period_bytes; ++ size_t nump = 0, numf = 0; ++ ++ if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) ++ return NULL; ++ nump = xt->nump; ++ numf = xt->numf; ++ size = xt->sgl[0].size; ++ period_bytes = size * nump; ++ buffer_bytes = size * numf; ++ ++ if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) ++ return NULL; ++ ++ src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); ++ dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); ++ ++ pl330_config_write(chan, &pch->slave_config, xt->dir); ++ ++ if (!pl330_prep_slave_fifo(pch, xt->dir)) ++ return NULL; ++ ++ for (i = 0; i < numf / nump; i++) { ++ desc = pl330_get_desc(pch); ++ if (!desc) { ++ unsigned long iflags; ++ ++ dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", ++ __func__, __LINE__); ++ ++ if (!first) ++ return NULL; ++ ++ spin_lock_irqsave(&pl330->pool_lock, iflags); ++ ++ 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, iflags); ++ ++ return NULL; ++ } ++ ++ switch (xt->dir) { ++ case DMA_MEM_TO_DEV: ++ desc->rqcfg.src_inc = 1; ++ desc->rqcfg.dst_inc = 0; ++ src = xt->src_start + period_bytes * i; ++ dst = pch->fifo_dma; ++ full_period_bytes = (size + src_icg) * nump; ++ break; ++ case DMA_DEV_TO_MEM: ++ desc->rqcfg.src_inc = 0; ++ desc->rqcfg.dst_inc = 1; ++ src = pch->fifo_dma; ++ dst = xt->dst_start + period_bytes * i; ++ full_period_bytes = (size + dst_icg) * nump; ++ break; ++ default: ++ break; ++ } ++ ++ desc->rqtype = xt->dir; ++ desc->rqcfg.brst_size = pch->burst_sz; ++ desc->rqcfg.brst_len = pch->burst_len; ++ desc->bytes_requested = full_period_bytes; ++ desc->sgl.size = size; ++ desc->sgl.src_icg = src_icg; ++ desc->sgl.dst_icg = dst_icg; ++ fill_px(&desc->px, dst, src, period_bytes); ++ ++ if (!first) ++ first = desc; ++ else ++ list_add_tail(&desc->node, &first->node); ++ } ++ ++ if (!desc) ++ return NULL; ++ ++ if (flags & DMA_PREP_REPEAT) ++ desc->cyclic = true; ++ ++ dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", ++ size, src_icg, dst_icg, nump, numf); ++ ++ return &desc->txd; ++} ++ + static struct dma_async_tx_descriptor * + pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +@@ -3221,12 +3373,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + 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_INTERLEAVE, pd->cap_mask); ++ dma_cap_set(DMA_REPEAT, pd->cap_mask); ++ dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); + } + + 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_cyclic = pl330_prep_dma_cyclic; ++ pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; + pd->device_tx_status = pl330_tx_status; + pd->device_prep_slave_sg = pl330_prep_slave_sg; + pd->device_config = pl330_config; +diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h +index b137fdb56093..63624c5836cf 100644 +--- a/include/linux/dmaengine.h ++++ b/include/linux/dmaengine.h +@@ -156,6 +156,7 @@ struct dma_interleaved_template { + bool src_sgl; + bool dst_sgl; + size_t numf; ++ size_t nump; + size_t frame_size; + struct data_chunk sgl[]; + }; +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch new file mode 100644 index 000000000000..9caad0107c4f --- /dev/null +++ b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch @@ -0,0 +1,47 @@ +From 5654890f22bc9f08ac2afd051c935ba8f8bc7e33 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:26:49 +0100 +Subject: [PATCH 2/2] rockchip/64: pl330: Fix data race on TX transferred bytes + reporting + +original source: https://patchwork.kernel.org/project/linux-rockchip/patch/20170302125710.14483-1-romain.perier@collabora.com/ +--- + drivers/dma/pl330.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index b49a3a6c4686..62003b67c231 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1872,6 +1872,7 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; ++ descdone->status = DONE; + if (descdone) { + if (!descdone->cyclic) { + thrd->req[active].desc = NULL; +@@ -1887,12 +1888,19 @@ static int pl330_update(struct pl330_dmac *pl330) + } + + /* Now that we are in no hurry, do the callbacks */ ++ struct dma_pl330_chan *pch; + while (!list_empty(&pl330->req_done)) { + descdone = list_first_entry(&pl330->req_done, + struct dma_pl330_desc, rqd); + list_del(&descdone->rqd); + spin_unlock_irqrestore(&pl330->lock, flags); +- dma_pl330_rqcb(descdone, PL330_ERR_NONE); ++ pch = descdone->pchan; ++ /* If desc aborted */ ++ if (!pch) { ++ spin_lock_irqsave(&pl330->lock, flags); ++ continue; ++ } ++ tasklet_schedule(&pch->task); + spin_lock_irqsave(&pl330->lock, flags); + } + +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch new file mode 100644 index 000000000000..15b348888936 --- /dev/null +++ b/patch/kernel/archive/rockchip-6.13/patches.armbian/general-pl330-04-bigger-mcode-buffer.patch @@ -0,0 +1,27 @@ +From f695d6bfdd099ec6f59a73aa792b97df80c72d54 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 26 Jan 2025 14:49:18 +0100 +Subject: [PATCH] increase pl330 microcode buffer size + +suggestion comes from the scatter/gather functionality as +proposed here: https://github.com/radxa/kernel/commit/ec0b65dbc59793426b6dc7af06ab6675f4a24940 +--- + 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 82a9fe88ad54..eb322c7ae5de 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -246,7 +246,7 @@ enum pl330_byteswap { + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +-#define MCODE_BUFF_PER_REQ 256 ++#define MCODE_BUFF_PER_REQ 512 + + /* Use this _only_ to wait on transient states */ + #define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip-6.13/series.conf b/patch/kernel/archive/rockchip-6.13/series.conf index 9d32a6f1fe3c..1a263bf68780 100644 --- a/patch/kernel/archive/rockchip-6.13/series.conf +++ b/patch/kernel/archive/rockchip-6.13/series.conf @@ -42,7 +42,12 @@ patches.armbian/general-dwc2-fix-wait-time.patch patches.armbian/general-dwc2-nak-gadget.patch patches.armbian/general-fix-reboot-from-kwiboo.patch + patches.armbian/general-increase-spdif-dma-burst.patch patches.armbian/general-linux-export-mm-trace-rss-stats.patch + patches.armbian/general-pl330-01-fix-periodic-transfers.patch + patches.armbian/general-pl330-02-add-support-for-interleaved-transfers.patch + patches.armbian/general-pl330-03-fix-data-race-on-tx-transfers.patch + patches.armbian/general-pl330-04-bigger-mcode-buffer.patch patches.armbian/general-rk322x-gpio-ir-driver.patch patches.armbian/general-rockchip-various-fixes.patch patches.armbian/ir-keymap-rk322x-box.patch diff --git a/patch/kernel/archive/rockchip64-6.12/general-increase-spdif-dma-burst.patch b/patch/kernel/archive/rockchip64-6.12/general-increase-spdif-dma-burst.patch new file mode 100644 index 000000000000..9d3a91b4dd94 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.12/general-increase-spdif-dma-burst.patch @@ -0,0 +1,25 @@ +From 379651eb82cf5966a40a5b931afc2fa91c6a311d Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:39:03 +0100 +Subject: [PATCH 2/2] rockchip: increase SPDIF max burst value to maximum + +--- + sound/soc/rockchip/rockchip_spdif.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +index d87c0e4f6f91..7a2cfecf6a94 100644 +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -329,7 +329,7 @@ static int rk_spdif_probe(struct platform_device *pdev) + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- spdif->playback_dma_data.maxburst = 4; ++ spdif->playback_dma_data.maxburst = 8; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.12/general-pl330-01-fix-periodic-transfers.patch b/patch/kernel/archive/rockchip64-6.12/general-pl330-01-fix-periodic-transfers.patch new file mode 100644 index 000000000000..844286d43db2 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.12/general-pl330-01-fix-periodic-transfers.patch @@ -0,0 +1,421 @@ +From fc0d09bf651fcab0998da4d187a91f64df419188 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:36:50 +0100 +Subject: [PATCH 1/2] pl330: fix dma engine periodic transfers + +--- + drivers/dma/pl330.c | 277 +++++++++++++++++++++++++++++--------------- + 1 file changed, 186 insertions(+), 91 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 82a9fe88ad54..ef197c4cfed4 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -239,6 +239,7 @@ 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 +@@ -455,9 +456,6 @@ struct dma_pl330_chan { + enum dma_data_direction dir; + struct dma_slave_config slave_config; + +- /* for cyclic capability */ +- bool cyclic; +- + /* for runtime pm tracking */ + bool active; + }; +@@ -545,6 +543,10 @@ 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 { +@@ -1368,6 +1370,108 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], + return off; + } + ++static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], ++ unsigned long bursts, const struct _xfer_spec *pxs, int ev) ++{ ++ unsigned int lcnt1, ljmp1; ++ int cyc, off = 0, num_dregs = 0; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ if (bursts > 256) { ++ lcnt1 = 256; ++ cyc = bursts / 256; ++ } else { ++ lcnt1 = bursts; ++ cyc = 1; ++ } ++ ++ /* 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); ++ } ++ ++ num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); ++ ++ if (num_dregs) { ++ off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); ++ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); ++ } ++ ++ off += _emit_SEV(dry_run, &buf[off], ev); ++ ++ return off; ++} ++ ++static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned int dry_run, ++ u8 buf[], unsigned long bursts, ++ const struct _xfer_spec *pxs, int ev) ++{ ++ int off, periods, residue, i; ++ unsigned int lcnt0, ljmp0, ljmpfe; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ off = 0; ++ ljmpfe = off; ++ lcnt0 = pxs->desc->num_periods; ++ periods = 1; ++ ++ while (lcnt0 > 256) { ++ periods++; ++ lcnt0 = pxs->desc->num_periods / periods; ++ } ++ ++ residue = pxs->desc->num_periods % periods; ++ ++ /* 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; ++ ++ for (i = 0; i < periods; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ lpend.cond = ALWAYS; ++ lpend.forever = false; ++ lpend.loop = 0; ++ lpend.bjump = off - ljmp0; ++ off += _emit_LPEND(dry_run, &buf[off], &lpend); ++ ++ for (i = 0; i < residue; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ 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) +@@ -1407,6 +1511,21 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + return off; + } + ++static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, ++ unsigned int 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. +@@ -1424,12 +1543,17 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + +- off += _setup_xfer(pl330, dry_run, &buf[off], pxs); ++ if (!pxs->desc->cyclic) { ++ off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + +- /* DMASEV peripheral/event */ +- off += _emit_SEV(dry_run, &buf[off], thrd->ev); +- /* DMAEND */ +- off += _emit_END(dry_run, &buf[off]); ++ /* DMASEV peripheral/event */ ++ off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ /* DMAEND */ ++ off += _emit_END(dry_run, &buf[off]); ++ } else { ++ off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], ++ pxs, thrd->ev); ++ } + + return off; + } +@@ -1703,15 +1827,17 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; +- thrd->req[active].desc = NULL; +- +- thrd->req_running = -1; +- +- /* Get going again ASAP */ +- pl330_start_thread(thrd); +- +- /* For now, just make a list of callbacks to be done */ +- list_add_tail(&descdone->rqd, &pl330->req_done); ++ if (descdone) { ++ if (!descdone->cyclic) { ++ thrd->req[active].desc = NULL; ++ thrd->req_running = -1; ++ /* Get going again ASAP */ ++ pl330_start_thread(thrd); ++ } ++ ++ /* For now, just make a list of callbacks to be done */ ++ list_add_tail(&descdone->rqd, &pl330->req_done); ++ } + } + } + +@@ -2076,12 +2202,25 @@ static void pl330_tasklet(struct tasklet_struct *t) + 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 (!pch->cyclic) ++ if (!desc->cyclic) { + dma_cookie_complete(&desc->txd); +- list_move_tail(&desc->node, &pch->completed_list); ++ list_move_tail(&desc->node, &pch->completed_list); ++ } else { ++ struct dmaengine_desc_callback cb; ++ ++ desc->status = BUSY; ++ dmaengine_desc_get_callback(&desc->txd, &cb); ++ ++ if (dmaengine_desc_callback_valid(&cb)) { ++ spin_unlock_irqrestore(&pch->lock, flags); ++ dmaengine_desc_callback_invoke(&cb, NULL); ++ spin_lock_irqsave(&pch->lock, flags); ++ } ++ } + } ++ } + + /* Try to submit a req imm. next to the last completed cookie */ + fill_queue(pch); +@@ -2107,20 +2246,8 @@ static void pl330_tasklet(struct tasklet_struct *t) + + dmaengine_desc_get_callback(&desc->txd, &cb); + +- 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); +- pl330_start_thread(pch->thread); +- spin_unlock(&pch->thread->dmac->lock); +- power_down = false; +- } +- } else { +- desc->status = FREE; +- list_move_tail(&desc->node, &pch->dmac->desc_pool); +- } ++ desc->status = FREE; ++ list_move_tail(&desc->node, &pch->dmac->desc_pool); + + dma_descriptor_unmap(&desc->txd); + +@@ -2168,7 +2295,6 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + spin_lock_irqsave(&pl330->lock, flags); + + dma_cookie_init(chan); +- pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +@@ -2367,8 +2493,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + pl330_release_channel(pch->thread); + pch->thread = NULL; + +- if (pch->cyclic) +- list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); ++ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + + spin_unlock_irqrestore(&pl330->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); +@@ -2431,7 +2556,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) ++ if (desc->status == DONE && !desc->cyclic) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = +@@ -2516,10 +2641,7 @@ 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); +@@ -2622,6 +2744,9 @@ 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; +@@ -2685,12 +2810,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, *first = NULL; ++ struct dma_pl330_desc *desc = 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; ++ dma_addr_t dst = 0; ++ dma_addr_t src = 0; + + if (len % period_len != 0) + return NULL; +@@ -2706,33 +2829,14 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + if (!pl330_prep_slave_fifo(pch, direction)) + return NULL; + +- for (i = 0; i < len / period_len; i++) { +- desc = pl330_get_desc(pch); +- if (!desc) { +- unsigned long iflags; +- +- dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", +- __func__, __LINE__); +- +- if (!first) +- return NULL; +- +- spin_lock_irqsave(&pl330->pool_lock, iflags); +- +- 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, iflags); +- +- 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; ++ } + +- switch (direction) { ++ switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; +@@ -2746,27 +2850,18 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + dst = dma_addr; + break; + default: +- break; +- } +- +- desc->rqtype = direction; +- desc->rqcfg.brst_size = pch->burst_sz; +- desc->rqcfg.brst_len = pch->burst_len; +- 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; ++ break; + } + +- if (!desc) +- return NULL; ++ 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); + +- pch->cyclic = true; ++ desc->cyclic = true; ++ desc->num_periods = len / period_len; ++ desc->txd.flags = flags; + + return &desc->txd; + } +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.12/general-pl330-02-add-support-for-interleaved-transfers.patch b/patch/kernel/archive/rockchip64-6.12/general-pl330-02-add-support-for-interleaved-transfers.patch new file mode 100644 index 000000000000..54f0dd9134f2 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.12/general-pl330-02-add-support-for-interleaved-transfers.patch @@ -0,0 +1,261 @@ +From e691c5c3feede95b4e159344aaea070fc428c847 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:23:50 +0100 +Subject: [PATCH 1/2] rockchip/64: pl330 - add support for interleaved + transfers + +original source: https://patchwork.kernel.org/project/linux-rockchip/cover/1712150304-60832-1-git-send-email-sugar.zhang@rock-chips.com/ +--- + drivers/dma/pl330.c | 168 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 162 insertions(+), 6 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index ef197c4cfed4..b49a3a6c4686 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -543,6 +543,8 @@ struct dma_pl330_desc { + unsigned peri:5; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; ++ /* interleaved size */ ++ struct data_chunk sgl; + + /* For cyclic capability */ + bool cyclic; +@@ -579,6 +581,22 @@ 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); ++ *((__le16 *)&buf[1]) = cpu_to_le16(val); ++ ++ 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) +@@ -1189,7 +1207,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + const struct _xfer_spec *pxs, int cyc, + enum pl330_cond cond) + { +- int off = 0; ++ int off = 0, i = 0, burstn = 1; + + /* + * do FLUSHP at beginning to clear any stale dma requests before the +@@ -1197,12 +1215,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + */ + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); ++ ++ if (pxs->desc->sgl.size) { ++ WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); ++ burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); ++ } ++ + while (cyc--) { +- off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +- off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); +- off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); ++ for (i = 0; i < burstn; i++) { ++ off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); ++ off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ } ++ ++ switch (pxs->desc->rqtype) { ++ case DMA_DEV_TO_MEM: ++ if (pxs->desc->sgl.dst_icg) ++ off += _emit_ADDH(dry_run, &buf[off], DST, ++ pxs->desc->sgl.dst_icg); ++ break; ++ case DMA_MEM_TO_DEV: ++ if (pxs->desc->sgl.src_icg) ++ off += _emit_ADDH(dry_run, &buf[off], SRC, ++ pxs->desc->sgl.src_icg); ++ break; ++ default: ++ WARN_ON(1); ++ break; ++ } + } + + return off; +@@ -1483,6 +1525,9 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + BRST_SIZE(ccr); + int off = 0; + ++ if (pxs->desc->sgl.size) ++ bursts = x->bytes / pxs->desc->sgl.size; ++ + while (bursts) { + c = bursts; + off += _loop(pl330, dry_run, &buf[off], &c, pxs); +@@ -2743,6 +2788,9 @@ 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->sgl.size = 0; ++ desc->sgl.src_icg = 0; ++ desc->sgl.dst_icg = 0; + + desc->cyclic = false; + desc->num_periods = 1; +@@ -2866,6 +2914,110 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + return &desc->txd; + } + ++static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( ++ struct dma_chan *chan, struct dma_interleaved_template *xt, ++ unsigned long flags) ++{ ++ 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; ++ size_t size, src_icg, dst_icg, period_bytes, buffer_bytes, full_period_bytes; ++ size_t nump = 0, numf = 0; ++ ++ if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) ++ return NULL; ++ nump = xt->nump; ++ numf = xt->numf; ++ size = xt->sgl[0].size; ++ period_bytes = size * nump; ++ buffer_bytes = size * numf; ++ ++ if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) ++ return NULL; ++ ++ src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); ++ dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); ++ ++ pl330_config_write(chan, &pch->slave_config, xt->dir); ++ ++ if (!pl330_prep_slave_fifo(pch, xt->dir)) ++ return NULL; ++ ++ for (i = 0; i < numf / nump; i++) { ++ desc = pl330_get_desc(pch); ++ if (!desc) { ++ unsigned long iflags; ++ ++ dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", ++ __func__, __LINE__); ++ ++ if (!first) ++ return NULL; ++ ++ spin_lock_irqsave(&pl330->pool_lock, iflags); ++ ++ 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, iflags); ++ ++ return NULL; ++ } ++ ++ switch (xt->dir) { ++ case DMA_MEM_TO_DEV: ++ desc->rqcfg.src_inc = 1; ++ desc->rqcfg.dst_inc = 0; ++ src = xt->src_start + period_bytes * i; ++ dst = pch->fifo_dma; ++ full_period_bytes = (size + src_icg) * nump; ++ break; ++ case DMA_DEV_TO_MEM: ++ desc->rqcfg.src_inc = 0; ++ desc->rqcfg.dst_inc = 1; ++ src = pch->fifo_dma; ++ dst = xt->dst_start + period_bytes * i; ++ full_period_bytes = (size + dst_icg) * nump; ++ break; ++ default: ++ break; ++ } ++ ++ desc->rqtype = xt->dir; ++ desc->rqcfg.brst_size = pch->burst_sz; ++ desc->rqcfg.brst_len = pch->burst_len; ++ desc->bytes_requested = full_period_bytes; ++ desc->sgl.size = size; ++ desc->sgl.src_icg = src_icg; ++ desc->sgl.dst_icg = dst_icg; ++ fill_px(&desc->px, dst, src, period_bytes); ++ ++ if (!first) ++ first = desc; ++ else ++ list_add_tail(&desc->node, &first->node); ++ } ++ ++ if (!desc) ++ return NULL; ++ ++ if (flags & DMA_PREP_REPEAT) ++ desc->cyclic = true; ++ ++ dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", ++ size, src_icg, dst_icg, nump, numf); ++ ++ return &desc->txd; ++} ++ + static struct dma_async_tx_descriptor * + pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +@@ -3221,12 +3373,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + 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_INTERLEAVE, pd->cap_mask); ++ dma_cap_set(DMA_REPEAT, pd->cap_mask); ++ dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); + } + + 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_cyclic = pl330_prep_dma_cyclic; ++ pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; + pd->device_tx_status = pl330_tx_status; + pd->device_prep_slave_sg = pl330_prep_slave_sg; + pd->device_config = pl330_config; +diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h +index b137fdb56093..63624c5836cf 100644 +--- a/include/linux/dmaengine.h ++++ b/include/linux/dmaengine.h +@@ -156,6 +156,7 @@ struct dma_interleaved_template { + bool src_sgl; + bool dst_sgl; + size_t numf; ++ size_t nump; + size_t frame_size; + struct data_chunk sgl[]; + }; +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.12/general-pl330-03-fix-data-race-on-tx-transfers.patch b/patch/kernel/archive/rockchip64-6.12/general-pl330-03-fix-data-race-on-tx-transfers.patch new file mode 100644 index 000000000000..9caad0107c4f --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.12/general-pl330-03-fix-data-race-on-tx-transfers.patch @@ -0,0 +1,47 @@ +From 5654890f22bc9f08ac2afd051c935ba8f8bc7e33 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:26:49 +0100 +Subject: [PATCH 2/2] rockchip/64: pl330: Fix data race on TX transferred bytes + reporting + +original source: https://patchwork.kernel.org/project/linux-rockchip/patch/20170302125710.14483-1-romain.perier@collabora.com/ +--- + drivers/dma/pl330.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index b49a3a6c4686..62003b67c231 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1872,6 +1872,7 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; ++ descdone->status = DONE; + if (descdone) { + if (!descdone->cyclic) { + thrd->req[active].desc = NULL; +@@ -1887,12 +1888,19 @@ static int pl330_update(struct pl330_dmac *pl330) + } + + /* Now that we are in no hurry, do the callbacks */ ++ struct dma_pl330_chan *pch; + while (!list_empty(&pl330->req_done)) { + descdone = list_first_entry(&pl330->req_done, + struct dma_pl330_desc, rqd); + list_del(&descdone->rqd); + spin_unlock_irqrestore(&pl330->lock, flags); +- dma_pl330_rqcb(descdone, PL330_ERR_NONE); ++ pch = descdone->pchan; ++ /* If desc aborted */ ++ if (!pch) { ++ spin_lock_irqsave(&pl330->lock, flags); ++ continue; ++ } ++ tasklet_schedule(&pch->task); + spin_lock_irqsave(&pl330->lock, flags); + } + +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.12/general-pl330-04-bigger-mcode-buffer.patch b/patch/kernel/archive/rockchip64-6.12/general-pl330-04-bigger-mcode-buffer.patch new file mode 100644 index 000000000000..15b348888936 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.12/general-pl330-04-bigger-mcode-buffer.patch @@ -0,0 +1,27 @@ +From f695d6bfdd099ec6f59a73aa792b97df80c72d54 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 26 Jan 2025 14:49:18 +0100 +Subject: [PATCH] increase pl330 microcode buffer size + +suggestion comes from the scatter/gather functionality as +proposed here: https://github.com/radxa/kernel/commit/ec0b65dbc59793426b6dc7af06ab6675f4a24940 +--- + 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 82a9fe88ad54..eb322c7ae5de 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -246,7 +246,7 @@ enum pl330_byteswap { + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +-#define MCODE_BUFF_PER_REQ 256 ++#define MCODE_BUFF_PER_REQ 512 + + /* Use this _only_ to wait on transient states */ + #define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.13/general-increase-spdif-dma-burst.patch b/patch/kernel/archive/rockchip64-6.13/general-increase-spdif-dma-burst.patch new file mode 100644 index 000000000000..9d3a91b4dd94 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.13/general-increase-spdif-dma-burst.patch @@ -0,0 +1,25 @@ +From 379651eb82cf5966a40a5b931afc2fa91c6a311d Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:39:03 +0100 +Subject: [PATCH 2/2] rockchip: increase SPDIF max burst value to maximum + +--- + sound/soc/rockchip/rockchip_spdif.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c +index d87c0e4f6f91..7a2cfecf6a94 100644 +--- a/sound/soc/rockchip/rockchip_spdif.c ++++ b/sound/soc/rockchip/rockchip_spdif.c +@@ -329,7 +329,7 @@ static int rk_spdif_probe(struct platform_device *pdev) + + spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR; + spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- spdif->playback_dma_data.maxburst = 4; ++ spdif->playback_dma_data.maxburst = 8; + + spdif->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, spdif); +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.13/general-pl330-01-fix-periodic-transfers.patch b/patch/kernel/archive/rockchip64-6.13/general-pl330-01-fix-periodic-transfers.patch new file mode 100644 index 000000000000..844286d43db2 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.13/general-pl330-01-fix-periodic-transfers.patch @@ -0,0 +1,421 @@ +From fc0d09bf651fcab0998da4d187a91f64df419188 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 12 Jan 2025 12:36:50 +0100 +Subject: [PATCH 1/2] pl330: fix dma engine periodic transfers + +--- + drivers/dma/pl330.c | 277 +++++++++++++++++++++++++++++--------------- + 1 file changed, 186 insertions(+), 91 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index 82a9fe88ad54..ef197c4cfed4 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -239,6 +239,7 @@ 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 +@@ -455,9 +456,6 @@ struct dma_pl330_chan { + enum dma_data_direction dir; + struct dma_slave_config slave_config; + +- /* for cyclic capability */ +- bool cyclic; +- + /* for runtime pm tracking */ + bool active; + }; +@@ -545,6 +543,10 @@ 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 { +@@ -1368,6 +1370,108 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], + return off; + } + ++static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], ++ unsigned long bursts, const struct _xfer_spec *pxs, int ev) ++{ ++ unsigned int lcnt1, ljmp1; ++ int cyc, off = 0, num_dregs = 0; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ if (bursts > 256) { ++ lcnt1 = 256; ++ cyc = bursts / 256; ++ } else { ++ lcnt1 = bursts; ++ cyc = 1; ++ } ++ ++ /* 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); ++ } ++ ++ num_dregs = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); ++ ++ if (num_dregs) { ++ off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); ++ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); ++ } ++ ++ off += _emit_SEV(dry_run, &buf[off], ev); ++ ++ return off; ++} ++ ++static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned int dry_run, ++ u8 buf[], unsigned long bursts, ++ const struct _xfer_spec *pxs, int ev) ++{ ++ int off, periods, residue, i; ++ unsigned int lcnt0, ljmp0, ljmpfe; ++ struct _arg_LPEND lpend; ++ struct pl330_xfer *x = &pxs->desc->px; ++ ++ off = 0; ++ ljmpfe = off; ++ lcnt0 = pxs->desc->num_periods; ++ periods = 1; ++ ++ while (lcnt0 > 256) { ++ periods++; ++ lcnt0 = pxs->desc->num_periods / periods; ++ } ++ ++ residue = pxs->desc->num_periods % periods; ++ ++ /* 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; ++ ++ for (i = 0; i < periods; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ lpend.cond = ALWAYS; ++ lpend.forever = false; ++ lpend.loop = 0; ++ lpend.bjump = off - ljmp0; ++ off += _emit_LPEND(dry_run, &buf[off], &lpend); ++ ++ for (i = 0; i < residue; i++) ++ off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); ++ ++ 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) +@@ -1407,6 +1511,21 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, + return off; + } + ++static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, ++ unsigned int 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. +@@ -1424,12 +1543,17 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, + /* DMAMOV CCR, ccr */ + off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); + +- off += _setup_xfer(pl330, dry_run, &buf[off], pxs); ++ if (!pxs->desc->cyclic) { ++ off += _setup_xfer(pl330, dry_run, &buf[off], pxs); + +- /* DMASEV peripheral/event */ +- off += _emit_SEV(dry_run, &buf[off], thrd->ev); +- /* DMAEND */ +- off += _emit_END(dry_run, &buf[off]); ++ /* DMASEV peripheral/event */ ++ off += _emit_SEV(dry_run, &buf[off], thrd->ev); ++ /* DMAEND */ ++ off += _emit_END(dry_run, &buf[off]); ++ } else { ++ off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], ++ pxs, thrd->ev); ++ } + + return off; + } +@@ -1703,15 +1827,17 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; +- thrd->req[active].desc = NULL; +- +- thrd->req_running = -1; +- +- /* Get going again ASAP */ +- pl330_start_thread(thrd); +- +- /* For now, just make a list of callbacks to be done */ +- list_add_tail(&descdone->rqd, &pl330->req_done); ++ if (descdone) { ++ if (!descdone->cyclic) { ++ thrd->req[active].desc = NULL; ++ thrd->req_running = -1; ++ /* Get going again ASAP */ ++ pl330_start_thread(thrd); ++ } ++ ++ /* For now, just make a list of callbacks to be done */ ++ list_add_tail(&descdone->rqd, &pl330->req_done); ++ } + } + } + +@@ -2076,12 +2202,25 @@ static void pl330_tasklet(struct tasklet_struct *t) + 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 (!pch->cyclic) ++ if (!desc->cyclic) { + dma_cookie_complete(&desc->txd); +- list_move_tail(&desc->node, &pch->completed_list); ++ list_move_tail(&desc->node, &pch->completed_list); ++ } else { ++ struct dmaengine_desc_callback cb; ++ ++ desc->status = BUSY; ++ dmaengine_desc_get_callback(&desc->txd, &cb); ++ ++ if (dmaengine_desc_callback_valid(&cb)) { ++ spin_unlock_irqrestore(&pch->lock, flags); ++ dmaengine_desc_callback_invoke(&cb, NULL); ++ spin_lock_irqsave(&pch->lock, flags); ++ } ++ } + } ++ } + + /* Try to submit a req imm. next to the last completed cookie */ + fill_queue(pch); +@@ -2107,20 +2246,8 @@ static void pl330_tasklet(struct tasklet_struct *t) + + dmaengine_desc_get_callback(&desc->txd, &cb); + +- 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); +- pl330_start_thread(pch->thread); +- spin_unlock(&pch->thread->dmac->lock); +- power_down = false; +- } +- } else { +- desc->status = FREE; +- list_move_tail(&desc->node, &pch->dmac->desc_pool); +- } ++ desc->status = FREE; ++ list_move_tail(&desc->node, &pch->dmac->desc_pool); + + dma_descriptor_unmap(&desc->txd); + +@@ -2168,7 +2295,6 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) + spin_lock_irqsave(&pl330->lock, flags); + + dma_cookie_init(chan); +- pch->cyclic = false; + + pch->thread = pl330_request_channel(pl330); + if (!pch->thread) { +@@ -2367,8 +2493,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) + pl330_release_channel(pch->thread); + pch->thread = NULL; + +- if (pch->cyclic) +- list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); ++ list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + + spin_unlock_irqrestore(&pl330->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); +@@ -2431,7 +2556,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) ++ if (desc->status == DONE && !desc->cyclic) + transferred = desc->bytes_requested; + else if (running && desc == running) + transferred = +@@ -2516,10 +2641,7 @@ 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); +@@ -2622,6 +2744,9 @@ 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; +@@ -2685,12 +2810,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, *first = NULL; ++ struct dma_pl330_desc *desc = 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; ++ dma_addr_t dst = 0; ++ dma_addr_t src = 0; + + if (len % period_len != 0) + return NULL; +@@ -2706,33 +2829,14 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + if (!pl330_prep_slave_fifo(pch, direction)) + return NULL; + +- for (i = 0; i < len / period_len; i++) { +- desc = pl330_get_desc(pch); +- if (!desc) { +- unsigned long iflags; +- +- dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", +- __func__, __LINE__); +- +- if (!first) +- return NULL; +- +- spin_lock_irqsave(&pl330->pool_lock, iflags); +- +- 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, iflags); +- +- 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; ++ } + +- switch (direction) { ++ switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; +@@ -2746,27 +2850,18 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + dst = dma_addr; + break; + default: +- break; +- } +- +- desc->rqtype = direction; +- desc->rqcfg.brst_size = pch->burst_sz; +- desc->rqcfg.brst_len = pch->burst_len; +- 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; ++ break; + } + +- if (!desc) +- return NULL; ++ 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); + +- pch->cyclic = true; ++ desc->cyclic = true; ++ desc->num_periods = len / period_len; ++ desc->txd.flags = flags; + + return &desc->txd; + } +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.13/general-pl330-02-add-support-for-interleaved-transfers.patch b/patch/kernel/archive/rockchip64-6.13/general-pl330-02-add-support-for-interleaved-transfers.patch new file mode 100644 index 000000000000..54f0dd9134f2 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.13/general-pl330-02-add-support-for-interleaved-transfers.patch @@ -0,0 +1,261 @@ +From e691c5c3feede95b4e159344aaea070fc428c847 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:23:50 +0100 +Subject: [PATCH 1/2] rockchip/64: pl330 - add support for interleaved + transfers + +original source: https://patchwork.kernel.org/project/linux-rockchip/cover/1712150304-60832-1-git-send-email-sugar.zhang@rock-chips.com/ +--- + drivers/dma/pl330.c | 168 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 162 insertions(+), 6 deletions(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index ef197c4cfed4..b49a3a6c4686 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -543,6 +543,8 @@ struct dma_pl330_desc { + unsigned peri:5; + /* Hook to attach to DMAC's list of reqs with due callback */ + struct list_head rqd; ++ /* interleaved size */ ++ struct data_chunk sgl; + + /* For cyclic capability */ + bool cyclic; +@@ -579,6 +581,22 @@ 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); ++ *((__le16 *)&buf[1]) = cpu_to_le16(val); ++ ++ 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) +@@ -1189,7 +1207,7 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + const struct _xfer_spec *pxs, int cyc, + enum pl330_cond cond) + { +- int off = 0; ++ int off = 0, i = 0, burstn = 1; + + /* + * do FLUSHP at beginning to clear any stale dma requests before the +@@ -1197,12 +1215,36 @@ static inline int _ldst_peripheral(struct pl330_dmac *pl330, + */ + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); ++ ++ if (pxs->desc->sgl.size) { ++ WARN_ON(BYTE_MOD_BURST_LEN(pxs->desc->sgl.size, pxs->ccr)); ++ burstn = BYTE_TO_BURST(pxs->desc->sgl.size, pxs->ccr); ++ } ++ + while (cyc--) { +- off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); +- off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); +- off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, +- pxs->desc->peri); ++ for (i = 0; i < burstn; i++) { ++ off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); ++ off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, ++ pxs->desc->peri); ++ } ++ ++ switch (pxs->desc->rqtype) { ++ case DMA_DEV_TO_MEM: ++ if (pxs->desc->sgl.dst_icg) ++ off += _emit_ADDH(dry_run, &buf[off], DST, ++ pxs->desc->sgl.dst_icg); ++ break; ++ case DMA_MEM_TO_DEV: ++ if (pxs->desc->sgl.src_icg) ++ off += _emit_ADDH(dry_run, &buf[off], SRC, ++ pxs->desc->sgl.src_icg); ++ break; ++ default: ++ WARN_ON(1); ++ break; ++ } + } + + return off; +@@ -1483,6 +1525,9 @@ static inline int _setup_loops(struct pl330_dmac *pl330, + BRST_SIZE(ccr); + int off = 0; + ++ if (pxs->desc->sgl.size) ++ bursts = x->bytes / pxs->desc->sgl.size; ++ + while (bursts) { + c = bursts; + off += _loop(pl330, dry_run, &buf[off], &c, pxs); +@@ -2743,6 +2788,9 @@ 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->sgl.size = 0; ++ desc->sgl.src_icg = 0; ++ desc->sgl.dst_icg = 0; + + desc->cyclic = false; + desc->num_periods = 1; +@@ -2866,6 +2914,110 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( + return &desc->txd; + } + ++static struct dma_async_tx_descriptor *pl330_prep_interleaved_dma( ++ struct dma_chan *chan, struct dma_interleaved_template *xt, ++ unsigned long flags) ++{ ++ 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; ++ size_t size, src_icg, dst_icg, period_bytes, buffer_bytes, full_period_bytes; ++ size_t nump = 0, numf = 0; ++ ++ if (!xt->numf || !xt->sgl[0].size || xt->frame_size != 1) ++ return NULL; ++ nump = xt->nump; ++ numf = xt->numf; ++ size = xt->sgl[0].size; ++ period_bytes = size * nump; ++ buffer_bytes = size * numf; ++ ++ if (flags & DMA_PREP_REPEAT && (!nump || (numf % nump))) ++ return NULL; ++ ++ src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); ++ dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); ++ ++ pl330_config_write(chan, &pch->slave_config, xt->dir); ++ ++ if (!pl330_prep_slave_fifo(pch, xt->dir)) ++ return NULL; ++ ++ for (i = 0; i < numf / nump; i++) { ++ desc = pl330_get_desc(pch); ++ if (!desc) { ++ unsigned long iflags; ++ ++ dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", ++ __func__, __LINE__); ++ ++ if (!first) ++ return NULL; ++ ++ spin_lock_irqsave(&pl330->pool_lock, iflags); ++ ++ 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, iflags); ++ ++ return NULL; ++ } ++ ++ switch (xt->dir) { ++ case DMA_MEM_TO_DEV: ++ desc->rqcfg.src_inc = 1; ++ desc->rqcfg.dst_inc = 0; ++ src = xt->src_start + period_bytes * i; ++ dst = pch->fifo_dma; ++ full_period_bytes = (size + src_icg) * nump; ++ break; ++ case DMA_DEV_TO_MEM: ++ desc->rqcfg.src_inc = 0; ++ desc->rqcfg.dst_inc = 1; ++ src = pch->fifo_dma; ++ dst = xt->dst_start + period_bytes * i; ++ full_period_bytes = (size + dst_icg) * nump; ++ break; ++ default: ++ break; ++ } ++ ++ desc->rqtype = xt->dir; ++ desc->rqcfg.brst_size = pch->burst_sz; ++ desc->rqcfg.brst_len = pch->burst_len; ++ desc->bytes_requested = full_period_bytes; ++ desc->sgl.size = size; ++ desc->sgl.src_icg = src_icg; ++ desc->sgl.dst_icg = dst_icg; ++ fill_px(&desc->px, dst, src, period_bytes); ++ ++ if (!first) ++ first = desc; ++ else ++ list_add_tail(&desc->node, &first->node); ++ } ++ ++ if (!desc) ++ return NULL; ++ ++ if (flags & DMA_PREP_REPEAT) ++ desc->cyclic = true; ++ ++ dev_dbg(chan->device->dev, "size: %zu, src_icg: %zu, dst_icg: %zu, nump: %zu, numf: %zu\n", ++ size, src_icg, dst_icg, nump, numf); ++ ++ return &desc->txd; ++} ++ + static struct dma_async_tx_descriptor * + pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +@@ -3221,12 +3373,16 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) + 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_INTERLEAVE, pd->cap_mask); ++ dma_cap_set(DMA_REPEAT, pd->cap_mask); ++ dma_cap_set(DMA_LOAD_EOT, pd->cap_mask); + } + + 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_cyclic = pl330_prep_dma_cyclic; ++ pd->device_prep_interleaved_dma = pl330_prep_interleaved_dma; + pd->device_tx_status = pl330_tx_status; + pd->device_prep_slave_sg = pl330_prep_slave_sg; + pd->device_config = pl330_config; +diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h +index b137fdb56093..63624c5836cf 100644 +--- a/include/linux/dmaengine.h ++++ b/include/linux/dmaengine.h +@@ -156,6 +156,7 @@ struct dma_interleaved_template { + bool src_sgl; + bool dst_sgl; + size_t numf; ++ size_t nump; + size_t frame_size; + struct data_chunk sgl[]; + }; +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.13/general-pl330-03-fix-data-race-on-tx-transfers.patch b/patch/kernel/archive/rockchip64-6.13/general-pl330-03-fix-data-race-on-tx-transfers.patch new file mode 100644 index 000000000000..9caad0107c4f --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.13/general-pl330-03-fix-data-race-on-tx-transfers.patch @@ -0,0 +1,47 @@ +From 5654890f22bc9f08ac2afd051c935ba8f8bc7e33 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Thu, 23 Jan 2025 20:26:49 +0100 +Subject: [PATCH 2/2] rockchip/64: pl330: Fix data race on TX transferred bytes + reporting + +original source: https://patchwork.kernel.org/project/linux-rockchip/patch/20170302125710.14483-1-romain.perier@collabora.com/ +--- + drivers/dma/pl330.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c +index b49a3a6c4686..62003b67c231 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -1872,6 +1872,7 @@ static int pl330_update(struct pl330_dmac *pl330) + + /* Detach the req */ + descdone = thrd->req[active].desc; ++ descdone->status = DONE; + if (descdone) { + if (!descdone->cyclic) { + thrd->req[active].desc = NULL; +@@ -1887,12 +1888,19 @@ static int pl330_update(struct pl330_dmac *pl330) + } + + /* Now that we are in no hurry, do the callbacks */ ++ struct dma_pl330_chan *pch; + while (!list_empty(&pl330->req_done)) { + descdone = list_first_entry(&pl330->req_done, + struct dma_pl330_desc, rqd); + list_del(&descdone->rqd); + spin_unlock_irqrestore(&pl330->lock, flags); +- dma_pl330_rqcb(descdone, PL330_ERR_NONE); ++ pch = descdone->pchan; ++ /* If desc aborted */ ++ if (!pch) { ++ spin_lock_irqsave(&pl330->lock, flags); ++ continue; ++ } ++ tasklet_schedule(&pch->task); + spin_lock_irqsave(&pl330->lock, flags); + } + +-- +2.43.0 + diff --git a/patch/kernel/archive/rockchip64-6.13/general-pl330-04-bigger-mcode-buffer.patch b/patch/kernel/archive/rockchip64-6.13/general-pl330-04-bigger-mcode-buffer.patch new file mode 100644 index 000000000000..15b348888936 --- /dev/null +++ b/patch/kernel/archive/rockchip64-6.13/general-pl330-04-bigger-mcode-buffer.patch @@ -0,0 +1,27 @@ +From f695d6bfdd099ec6f59a73aa792b97df80c72d54 Mon Sep 17 00:00:00 2001 +From: Paolo Sabatino +Date: Sun, 26 Jan 2025 14:49:18 +0100 +Subject: [PATCH] increase pl330 microcode buffer size + +suggestion comes from the scatter/gather functionality as +proposed here: https://github.com/radxa/kernel/commit/ec0b65dbc59793426b6dc7af06ab6675f4a24940 +--- + 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 82a9fe88ad54..eb322c7ae5de 100644 +--- a/drivers/dma/pl330.c ++++ b/drivers/dma/pl330.c +@@ -246,7 +246,7 @@ enum pl330_byteswap { + * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req + * should be enough for P<->M and M<->M respectively. + */ +-#define MCODE_BUFF_PER_REQ 256 ++#define MCODE_BUFF_PER_REQ 512 + + /* Use this _only_ to wait on transient states */ + #define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); +-- +2.43.0 +