* [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma
@ 2026-11-02 9:06 Peter Robinson
2026-11-02 9:06 ` [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712 Peter Robinson
` (8 more replies)
0 siblings, 9 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
The upstream bcm2835-dma driver only handles the older BCM2835 SoCs
up to the Raspberry Pi 3. The later BCM2711/BCM2712 add extra channels
that support 40-bit DMA4 channels so add support so this works upstream.
The first patch updates the bindings, them patches 2-5 do some
refactoring and add the support for the newer SoCs.
Finally patches 6 and 7 add the nodes to the base device tree and
patches 8 and 9 enable it and configure HDMI audio to use them as
per the down stream device trees.
The series was assisted considerably by Claude Opus 5.5 based on the
various downstream Raspberry Pi patches and tested on a number of
RPi5B revC/D, RPi4B and a RPi3B+ all running Fedora 45. Checkpatch
and dtbs_check pass clean.
Peter Robinson (9):
dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712
dmaengine: bcm2835: Refactor control block handling
dmaengine: bcm2835: Support controllers split across several nodes
dmaengine: bcm2835: Add BCM2711 40-bit channel support
dmaengine: bcm2835: Add BCM2712 support
ARM: dts: bcm2711: Add 40-bit DMA controller
ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio
arm64: dts: broadcom: bcm2712: Add DMA controllers
arm64: dts: broadcom: bcm2712-rpi-5-b: Enable HDMI audio
.../bindings/dma/brcm,bcm2835-dma.yaml | 31 +-
arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi | 8 +
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 +
.../boot/dts/broadcom/bcm2712-d-rpi-5-b.dts | 8 +
.../dts/broadcom/bcm2712-rpi-5-b-base.dtsi | 10 +
arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 40 ++
drivers/dma/bcm2835-dma.c | 496 +++++++++++++++---
7 files changed, 534 insertions(+), 75 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 19:39 ` Conor Dooley
2026-11-02 9:06 ` [PATCH 2/9] dmaengine: bcm2835: Refactor control block handling Peter Robinson
` (7 subsequent siblings)
8 siblings, 1 reply; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Add compatibles for the BCM2711 40-bit DMA4 channels, which are
described by a separate node covering channels 11-14, and for the
BCM2712 DMA controllers. The register address of a node identifies its
first channel, allowing a controller to be split across nodes.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
---
.../bindings/dma/brcm,bcm2835-dma.yaml | 31 +++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
index c9b9a5490826c..31d97a10a0ea9 100644
--- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
@@ -9,18 +9,27 @@ title: BCM2835 DMA controller
maintainers:
- Nicolas Saenz Julienne <nsaenz@kernel.org>
-description:
+description: |
The BCM2835 DMA controller has 16 channels in total. Only the lower
13 channels have an associated IRQ. Some arbitrary channels are used by the
VideoCore firmware (1,3,6,7 in the current firmware version). The channels
0, 2 and 3 have special functionality and should not be used by the driver.
+ BCM2711 channels 11 to 14 are 40-bit capable DMA4 channels, described by a
+ separate brcm,bcm2711-dma node. BCM2712 has 40-bit addressing on all
+ channels, with channels 6 to 11 being DMA4 channels. A controller may be
+ split across several nodes, the register address identifies the first
+ channel of a node.
+
allOf:
- $ref: dma-controller.yaml#
properties:
compatible:
- const: brcm,bcm2835-dma
+ enum:
+ - brcm,bcm2835-dma
+ - brcm,bcm2711-dma
+ - brcm,bcm2712-dma
reg:
maxItems: 1
@@ -99,4 +108,22 @@ examples:
brcm,dma-channel-mask = <0x7f35>;
};
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ dma-controller@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
+ reg = <0x7e007b00 0x400>;
+ interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma11",
+ "dma12",
+ "dma13",
+ "dma14";
+ #dma-cells = <1>;
+ brcm,dma-channel-mask = <0x7000>;
+ };
+
...
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/9] dmaengine: bcm2835: Refactor control block handling
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
2026-11-02 9:06 ` [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712 Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:06 ` [PATCH 3/9] dmaengine: bcm2835: Support controllers split across several nodes Peter Robinson
` (6 subsequent siblings)
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Access control blocks through small helpers and pass struct
bcm2835_chan to the internal chain builders, and add per-compatible
match data holding the DMA mask. This prepares for channels with a
different control block layout.
No functional change.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
drivers/dma/bcm2835-dma.c | 185 +++++++++++++++++++++++++++-----------
1 file changed, 134 insertions(+), 51 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 06d830d368822..96a39eb24559f 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -37,17 +37,27 @@
#define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
#define BCM2835_DMA_CHAN_NAME_SIZE 8
+/**
+ * struct bcm2835_dma_cfg - SoC specific controller properties
+ * @dma_mask: addressing capability of the controller
+ */
+struct bcm2835_dma_cfg {
+ u64 dma_mask;
+};
+
/**
* struct bcm2835_dmadev - BCM2835 DMA controller
* @ddev: DMA device
* @base: base address of register map
* @zero_page: bus address of zero page (to detect transactions copying from
* zero page and avoid accessing memory if so)
+ * @cfg: SoC specific controller properties
*/
struct bcm2835_dmadev {
struct dma_device ddev;
void __iomem *base;
dma_addr_t zero_page;
+ const struct bcm2835_dma_cfg *cfg;
};
struct bcm2835_dma_cb {
@@ -172,6 +182,10 @@ struct bcm2835_desc {
#define MAX_DMA_LEN SZ_1G
#define MAX_LITE_DMA_LEN (SZ_64K - 4)
+static const struct bcm2835_dma_cfg bcm2835_dma_cfg = {
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
{
/* lite and normal channels have different max frame length */
@@ -201,6 +215,76 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc(
return container_of(t, struct bcm2835_desc, vd.tx);
}
+static void bcm2835_dma_cb_set_src(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e, dma_addr_t addr)
+{
+ e->cb->src = addr;
+}
+
+static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e, dma_addr_t addr)
+{
+ e->cb->dst = addr;
+}
+
+static void bcm2835_dma_cb_init(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e, u32 info,
+ dma_addr_t src, dma_addr_t dst)
+{
+ struct bcm2835_dma_cb *cb = e->cb;
+
+ cb->info = info;
+ cb->length = 0;
+ cb->stride = 0;
+ cb->next = 0;
+
+ bcm2835_dma_cb_set_src(c, e, src);
+ bcm2835_dma_cb_set_dst(c, e, dst);
+}
+
+static u32 bcm2835_dma_cb_get_len(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e)
+{
+ return e->cb->length;
+}
+
+static void bcm2835_dma_cb_set_len(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e, u32 len)
+{
+ e->cb->length = len;
+}
+
+static void bcm2835_dma_cb_add_info(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e, u32 info)
+{
+ e->cb->info |= info;
+}
+
+static void bcm2835_dma_cb_set_next(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e,
+ dma_addr_t next)
+{
+ e->cb->next = next;
+}
+
+/* Memory side address of a control block */
+static dma_addr_t bcm2835_dma_cb_mem_addr(struct bcm2835_chan *c,
+ struct bcm2835_cb_entry *e,
+ enum dma_transfer_direction dir)
+{
+ return dir == DMA_DEV_TO_MEM ? e->cb->dst : e->cb->src;
+}
+
+static dma_addr_t bcm2835_dma_get_pos(struct bcm2835_chan *c,
+ enum dma_transfer_direction dir)
+{
+ void __iomem *base = c->chan_base;
+
+ if (dir == DMA_DEV_TO_MEM)
+ return readl(base + BCM2835_DMA_DEST_AD);
+ return readl(base + BCM2835_DMA_SOURCE_AD);
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -220,16 +304,18 @@ static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
static void bcm2835_dma_create_cb_set_length(
struct bcm2835_chan *chan,
- struct bcm2835_dma_cb *control_block,
+ struct bcm2835_cb_entry *cb_entry,
size_t len,
size_t period_len,
size_t *total_len,
u32 finalextrainfo)
{
size_t max_len = bcm2835_dma_max_frame_length(chan);
+ u32 cb_len;
/* set the length taking lite-channel limitations into account */
- control_block->length = min_t(u32, len, max_len);
+ cb_len = min_t(u32, len, max_len);
+ bcm2835_dma_cb_set_len(chan, cb_entry, cb_len);
/* finished if we have no period_length */
if (!period_len)
@@ -244,26 +330,26 @@ static void bcm2835_dma_create_cb_set_length(
*/
/* have we filled in period_length yet? */
- if (*total_len + control_block->length < period_len) {
+ if (*total_len + cb_len < period_len) {
/* update number of bytes in this period so far */
- *total_len += control_block->length;
+ *total_len += cb_len;
return;
}
/* calculate the length that remains to reach period_length */
- control_block->length = period_len - *total_len;
+ bcm2835_dma_cb_set_len(chan, cb_entry, period_len - *total_len);
/* reset total_length for next period */
*total_len = 0;
/* add extrainfo bits in info */
- control_block->info |= finalextrainfo;
+ bcm2835_dma_cb_add_info(chan, cb_entry, finalextrainfo);
}
/**
* bcm2835_dma_create_cb_chain - create a control block and fills data in
*
- * @chan: the @dma_chan for which we run this
+ * @c: the channel for which we run this
* @direction: the direction in which we transfer
* @cyclic: it is a cyclic transfer
* @info: the default info bits to apply per controlblock
@@ -281,17 +367,16 @@ static void bcm2835_dma_create_cb_set_length(
* @gfp: the GFP flag to use for allocation
*/
static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
- struct dma_chan *chan, enum dma_transfer_direction direction,
+ struct bcm2835_chan *c, enum dma_transfer_direction direction,
bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
dma_addr_t src, dma_addr_t dst, size_t buf_len,
size_t period_len, gfp_t gfp)
{
- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
size_t len = buf_len, total_len;
size_t frame;
struct bcm2835_desc *d;
struct bcm2835_cb_entry *cb_entry;
- struct bcm2835_dma_cb *control_block;
+ u32 cb_len;
if (!frames)
return NULL;
@@ -317,40 +402,38 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
goto error_cb;
/* fill in the control block */
- control_block = cb_entry->cb;
- control_block->info = info;
- control_block->src = src;
- control_block->dst = dst;
- control_block->stride = 0;
- control_block->next = 0;
+ bcm2835_dma_cb_init(c, cb_entry, info, src, dst);
+
/* set up length in control_block if requested */
if (buf_len) {
/* calculate length honoring period_length */
bcm2835_dma_create_cb_set_length(
- c, control_block,
+ c, cb_entry,
len, period_len, &total_len,
cyclic ? finalextrainfo : 0);
-
- /* calculate new remaining length */
- len -= control_block->length;
}
+ cb_len = bcm2835_dma_cb_get_len(c, cb_entry);
+
+ /* calculate new remaining length */
+ len -= cb_len;
/* link this the last controlblock */
if (frame)
- d->cb_list[frame - 1].cb->next = cb_entry->paddr;
+ bcm2835_dma_cb_set_next(c, &d->cb_list[frame - 1],
+ cb_entry->paddr);
/* update src and dst and length */
if (src && (info & BCM2835_DMA_S_INC))
- src += control_block->length;
+ src += cb_len;
if (dst && (info & BCM2835_DMA_D_INC))
- dst += control_block->length;
+ dst += cb_len;
/* Length of total transfer */
- d->size += control_block->length;
+ d->size += cb_len;
}
/* the last frame requires extra flags */
- d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
+ bcm2835_dma_cb_add_info(c, &d->cb_list[d->frames - 1], finalextrainfo);
/* detect a size mismatch */
if (buf_len && (d->size != buf_len))
@@ -364,14 +447,13 @@ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
}
static void bcm2835_dma_fill_cb_chain_with_sg(
- struct dma_chan *chan,
+ struct bcm2835_chan *c,
enum dma_transfer_direction direction,
struct bcm2835_cb_entry *cb,
struct scatterlist *sgl,
unsigned int sg_len)
{
- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
- size_t len, max_len;
+ size_t len, max_len, cb_len;
unsigned int i;
dma_addr_t addr;
struct scatterlist *sgent;
@@ -380,12 +462,13 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
for_each_sg(sgl, sgent, sg_len, i) {
for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
len > 0;
- addr += cb->cb->length, len -= cb->cb->length, cb++) {
+ addr += cb_len, len -= cb_len, cb++) {
if (direction == DMA_DEV_TO_MEM)
- cb->cb->dst = addr;
+ bcm2835_dma_cb_set_dst(c, cb, addr);
else
- cb->cb->src = addr;
- cb->cb->length = min(len, max_len);
+ bcm2835_dma_cb_set_src(c, cb, addr);
+ cb_len = min(len, max_len);
+ bcm2835_dma_cb_set_len(c, cb, cb_len);
}
}
}
@@ -524,14 +607,9 @@ static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr)
size_t size;
for (size = i = 0; i < d->frames; i++) {
- struct bcm2835_dma_cb *control_block = d->cb_list[i].cb;
- size_t this_size = control_block->length;
- dma_addr_t dma;
-
- if (d->dir == DMA_DEV_TO_MEM)
- dma = control_block->dst;
- else
- dma = control_block->src;
+ struct bcm2835_cb_entry *e = &d->cb_list[i];
+ size_t this_size = bcm2835_dma_cb_get_len(d->c, e);
+ dma_addr_t dma = bcm2835_dma_cb_mem_addr(d->c, e, d->dir);
if (size)
size += this_size;
@@ -563,10 +641,8 @@ static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan,
struct bcm2835_desc *d = c->desc;
dma_addr_t pos;
- if (d->dir == DMA_MEM_TO_DEV)
- pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
- else if (d->dir == DMA_DEV_TO_MEM)
- pos = readl(c->chan_base + BCM2835_DMA_DEST_AD);
+ if (is_slave_direction(d->dir))
+ pos = bcm2835_dma_get_pos(c, d->dir);
else
pos = 0;
@@ -611,7 +687,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
frames = bcm2835_dma_frames_for_length(len, max_len);
/* allocate the CB chain - this also fills in the pointers */
- d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
+ d = bcm2835_dma_create_cb_chain(c, DMA_MEM_TO_MEM, false,
info, extra, frames,
src, dst, len, 0, GFP_KERNEL);
if (!d)
@@ -658,7 +734,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
frames = sg_nents_for_dma(sgl, sg_len, bcm2835_dma_max_frame_length(c));
/* allocate the CB chain */
- d = bcm2835_dma_create_cb_chain(chan, direction, false,
+ d = bcm2835_dma_create_cb_chain(c, direction, false,
info, extra,
frames, src, dst, 0, 0,
GFP_NOWAIT);
@@ -666,7 +742,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
return NULL;
/* fill in frames with scatterlist pointers */
- bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list,
+ bcm2835_dma_fill_cb_chain_with_sg(c, direction, d->cb_list,
sgl, sg_len);
return vchan_tx_prep(&c->vc, &d->vd, flags);
@@ -745,7 +821,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
* note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
* implementation calls prep_dma_cyclic with interrupts disabled.
*/
- d = bcm2835_dma_create_cb_chain(chan, direction, true,
+ d = bcm2835_dma_create_cb_chain(c, direction, true,
info, extra,
frames, src, dst, buf_len,
period_len, GFP_NOWAIT);
@@ -753,7 +829,8 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
return NULL;
/* wrap around into a loop */
- d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
+ bcm2835_dma_cb_set_next(c, &d->cb_list[d->frames - 1],
+ d->cb_list[0].paddr);
return vchan_tx_prep(&c->vc, &d->vd, flags);
}
@@ -837,7 +914,7 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
}
static const struct of_device_id bcm2835_dma_of_match[] = {
- { .compatible = "brcm,bcm2835-dma", },
+ { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
@@ -881,6 +958,7 @@ static const struct dev_pm_ops bcm2835_dma_pm_ops = {
static int bcm2835_dma_probe(struct platform_device *pdev)
{
+ const struct bcm2835_dma_cfg *cfg;
struct bcm2835_dmadev *od;
void __iomem *base;
int rc;
@@ -890,10 +968,14 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
uint32_t chans_available;
char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
+ cfg = of_device_get_match_data(&pdev->dev);
+ if (!cfg)
+ return -EINVAL;
+
if (!pdev->dev.dma_mask)
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
- rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ rc = dma_set_mask_and_coherent(&pdev->dev, cfg->dma_mask);
if (rc) {
dev_err(&pdev->dev, "Unable to set DMA mask\n");
return rc;
@@ -910,6 +992,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
return PTR_ERR(base);
od->base = base;
+ od->cfg = cfg;
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 3/9] dmaengine: bcm2835: Support controllers split across several nodes
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
2026-11-02 9:06 ` [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712 Peter Robinson
2026-11-02 9:06 ` [PATCH 2/9] dmaengine: bcm2835: Refactor control block handling Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:06 ` [PATCH 4/9] dmaengine: bcm2835: Add BCM2711 40-bit channel support Peter Robinson
` (5 subsequent siblings)
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Derive the index of the first channel from the register resource and
only probe the channels it covers, so a controller can be described by
several nodes. Initialise all IRQ slots, as the shared IRQ check scans
the whole array.
Existing device trees start at channel 0 and are unaffected.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
drivers/dma/bcm2835-dma.c | 34 +++++++++++++++++++++++-----------
1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 96a39eb24559f..83ad8815759e8 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -52,12 +52,14 @@ struct bcm2835_dma_cfg {
* @zero_page: bus address of zero page (to detect transactions copying from
* zero page and avoid accessing memory if so)
* @cfg: SoC specific controller properties
+ * @first_chan: hardware index of the first channel covered by @base
*/
struct bcm2835_dmadev {
struct dma_device ddev;
void __iomem *base;
dma_addr_t zero_page;
const struct bcm2835_dma_cfg *cfg;
+ unsigned int first_chan;
};
struct bcm2835_dma_cb {
@@ -175,8 +177,8 @@ struct bcm2835_desc {
#define BCM2835_DMA_DATA_TYPE_S128 16
/* Valid only for channels 0 - 14, 15 has its own base address */
-#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */
-#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
+#define BCM2835_DMA_CHAN_SIZE 0x100
+#define BCM2835_DMA_CHAN(n) ((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */
/* the max dma length for different channels */
#define MAX_DMA_LEN SZ_1G
@@ -886,7 +888,7 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
c->vc.desc_free = bcm2835_dma_desc_free;
vchan_init(&c->vc, &d->ddev);
- c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id);
+ c->chan_base = d->base + BCM2835_DMA_CHAN(chan_id - d->first_chan);
c->ch = chan_id;
c->irq_number = irq;
c->irq_flags = irq_flags;
@@ -960,6 +962,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
{
const struct bcm2835_dma_cfg *cfg;
struct bcm2835_dmadev *od;
+ struct resource *res;
void __iomem *base;
int rc;
int i, j;
@@ -967,6 +970,7 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
int irq_flags;
uint32_t chans_available;
char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
+ unsigned int chan_start, chan_end;
cfg = of_device_get_match_data(&pdev->dev);
if (!cfg)
@@ -987,12 +991,18 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
- base = devm_platform_ioremap_resource(pdev, 0);
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
+ chan_start = (res->start / BCM2835_DMA_CHAN_SIZE) & 0xf;
+ chan_end = min_t(unsigned int,
+ chan_start + resource_size(res) / BCM2835_DMA_CHAN_SIZE,
+ BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1);
+
od->base = base;
od->cfg = cfg;
+ od->first_chan = chan_start;
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
@@ -1037,12 +1047,13 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
}
/* get irqs for each channel that we support */
- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+ for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++)
+ irq[i] = -1;
+
+ for (i = chan_start; i < chan_end; i++) {
/* skip masked out channels */
- if (!(chans_available & (1 << i))) {
- irq[i] = -1;
+ if (!(chans_available & (1 << i)))
continue;
- }
/* get the named irq */
snprintf(chan_name, sizeof(chan_name), "dma%i", i);
@@ -1057,11 +1068,11 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
* in case of channel >= 11
* use the 11th interrupt and that is shared
*/
- irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
+ irq[i] = platform_get_irq(pdev, min(i - chan_start, 11U));
}
/* get irqs for each channel */
- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
+ for (i = chan_start; i < chan_end; i++) {
/* skip channels without irq */
if (irq[i] < 0)
continue;
@@ -1080,7 +1091,8 @@ static int bcm2835_dma_probe(struct platform_device *pdev)
goto err_no_dma;
}
- dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i);
+ dev_dbg(&pdev->dev, "Initialized DMA channels %u-%u\n", chan_start,
+ chan_end - 1);
/* Device-tree DMA controller registration */
rc = of_dma_controller_register(pdev->dev.of_node,
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 4/9] dmaengine: bcm2835: Add BCM2711 40-bit channel support
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (2 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 3/9] dmaengine: bcm2835: Support controllers split across several nodes Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:06 ` [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support Peter Robinson
` (4 subsequent siblings)
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
BCM2711 channels 11-14 are DMA4 engines with a 40-bit capable register
and control block layout. They see the legacy 0x7c000000 peripheral
window at 0x4_7c000000, so translate client FIFO addresses accordingly.
Add the brcm,bcm2711-dma compatible for covering these channels.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
drivers/dma/bcm2835-dma.c | 278 +++++++++++++++++++++++++++++++++++---
1 file changed, 256 insertions(+), 22 deletions(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 83ad8815759e8..35f8c47dea89f 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -17,6 +17,7 @@
* MARVELL MMP Peripheral DMA Driver
* Copyright 2012 Marvell International Ltd.
*/
+#include <linux/bitfield.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
@@ -40,9 +41,14 @@
/**
* struct bcm2835_dma_cfg - SoC specific controller properties
* @dma_mask: addressing capability of the controller
+ * @chan_40bit_mask: channels implementing the BCM2711 40-bit register layout
+ * @periph_offset: offset at which channels with extended addressing see the
+ * legacy 0x7c000000 - 0x7fffffff peripheral window
*/
struct bcm2835_dma_cfg {
u64 dma_mask;
+ u32 chan_40bit_mask;
+ u64 periph_offset;
};
/**
@@ -72,8 +78,25 @@ struct bcm2835_dma_cb {
uint32_t pad[2];
};
+/* Control block layout used by BCM2711 40-bit channels */
+struct bcm2711_dma40_cb {
+ u32 ti;
+ u32 src;
+ u32 srci;
+ u32 dst;
+ u32 dsti;
+ u32 len;
+ u32 next_cb;
+ u32 rsvd;
+};
+
+static_assert(sizeof(struct bcm2835_dma_cb) == sizeof(struct bcm2711_dma40_cb));
+
struct bcm2835_cb_entry {
- struct bcm2835_dma_cb *cb;
+ union {
+ struct bcm2835_dma_cb *cb;
+ struct bcm2711_dma40_cb *cb40;
+ };
dma_addr_t paddr;
};
@@ -92,6 +115,9 @@ struct bcm2835_chan {
unsigned int irq_flags;
bool is_lite_channel;
+ bool is_40bit_channel;
+ bool ext_addr;
+ u64 periph_offset;
};
struct bcm2835_desc {
@@ -148,7 +174,9 @@ struct bcm2835_desc {
#define BCM2835_DMA_S_WIDTH BIT(9) /* 128bit writes if set */
#define BCM2835_DMA_S_DREQ BIT(10) /* enable SREQ for source */
#define BCM2835_DMA_S_IGNORE BIT(11) /* ignore source reads - read 0 */
+#define BCM2835_DMA_BURST_LENGTH_MASK GENMASK(15, 12)
#define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12)
+#define BCM2835_DMA_PER_MAP_MASK GENMASK(20, 16)
#define BCM2835_DMA_PER_MAP(x) ((x & 31) << 16) /* REQ source */
#define BCM2835_DMA_WAIT(x) ((x & 31) << 21) /* add DMA-wait cycles */
#define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
@@ -184,10 +212,65 @@ struct bcm2835_desc {
#define MAX_DMA_LEN SZ_1G
#define MAX_LITE_DMA_LEN (SZ_64K - 4)
+/* Legacy peripheral bus window used by DMA clients for FIFO addresses */
+#define BCM2835_DMA_PERIPH_BASE 0x7c000000
+#define BCM2835_DMA_PERIPH_END 0x80000000
+
+/* BCM2711 40-bit channel registers */
+#define BCM2711_DMA40_CS 0x00
+#define BCM2711_DMA40_CB 0x04
+#define BCM2711_DMA40_DEBUG 0x0c
+#define BCM2711_DMA40_SRC 0x14
+#define BCM2711_DMA40_SRCI 0x18
+#define BCM2711_DMA40_DEST 0x1c
+#define BCM2711_DMA40_DESTI 0x20
+
+/* BCM2711 40-bit CS bits */
+#define BCM2711_DMA40_ACTIVE BIT(0)
+#define BCM2711_DMA40_END BIT(1)
+#define BCM2711_DMA40_INT BIT(2)
+#define BCM2711_DMA40_DREQ BIT(3)
+#define BCM2711_DMA40_RD_PAUSED BIT(4)
+#define BCM2711_DMA40_WR_PAUSED BIT(5)
+#define BCM2711_DMA40_DREQ_PAUSED BIT(6)
+#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7)
+#define BCM2711_DMA40_PROT GENMASK(9, 8) /* secure, supervisor */
+#define BCM2711_DMA40_ERR BIT(10)
+#define BCM2711_DMA40_TRANSACTIONS BIT(25)
+#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28)
+#define BCM2711_DMA40_DISDEBUG BIT(29)
+#define BCM2711_DMA40_ABORT BIT(30)
+#define BCM2711_DMA40_HALT BIT(31)
+
+/* BCM2711 40-bit TI bits */
+#define BCM2711_DMA40_INTEN BIT(0)
+#define BCM2711_DMA40_TDMODE BIT(1) /* 2D-Mode */
+#define BCM2711_DMA40_WAIT_RESP BIT(2) /* wait for AXI write ack */
+#define BCM2711_DMA40_WAIT_RD_RESP BIT(3) /* wait for AXI read to complete */
+#define BCM2711_DMA40_PER_MAP(x) (((x) & 31) << 9) /* REQ source */
+#define BCM2711_DMA40_S_DREQ BIT(14)
+#define BCM2711_DMA40_D_DREQ BIT(15)
+
+/* BCM2711 40-bit SRCI/DESTI bits */
+#define BCM2711_DMA40_ADDR_HI GENMASK(7, 0)
+#define BCM2711_DMA40_BURST_LEN(x) (((x) & 15) << 8)
+#define BCM2711_DMA40_INC BIT(12)
+#define BCM2711_DMA40_SIZE_128 FIELD_PREP_CONST(GENMASK(14, 13), 2)
+#define BCM2711_DMA40_IGNORE BIT(15)
+
+/* BCM2711 40-bit DEBUG bits */
+#define BCM2711_DMA40_DEBUG_RESET BIT(23)
+
static const struct bcm2835_dma_cfg bcm2835_dma_cfg = {
.dma_mask = DMA_BIT_MASK(32),
};
+static const struct bcm2835_dma_cfg bcm2711_dma_cfg = {
+ .dma_mask = DMA_BIT_MASK(36),
+ .chan_40bit_mask = GENMASK(14, 11),
+ .periph_offset = 0x400000000ULL,
+};
+
static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
{
/* lite and normal channels have different max frame length */
@@ -217,28 +300,95 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc(
return container_of(t, struct bcm2835_desc, vd.tx);
}
+/* Translate legacy BCM2835 TI bits into the 40-bit channel TI format */
+static u32 bcm2711_dma40_ti(u32 info)
+{
+ u32 ti = BCM2711_DMA40_PER_MAP(FIELD_GET(BCM2835_DMA_PER_MAP_MASK, info));
+
+ if (info & BCM2835_DMA_INT_EN)
+ ti |= BCM2711_DMA40_INTEN;
+ if (info & BCM2835_DMA_WAIT_RESP)
+ ti |= BCM2711_DMA40_WAIT_RESP;
+ if (info & BCM2835_DMA_S_DREQ)
+ ti |= BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP;
+ if (info & BCM2835_DMA_D_DREQ)
+ ti |= BCM2711_DMA40_D_DREQ;
+
+ return ti;
+}
+
+static u32 bcm2711_dma40_xi(u32 info, u32 inc, u32 width, u32 ignore)
+{
+ u32 xi = BCM2711_DMA40_BURST_LEN(FIELD_GET(BCM2835_DMA_BURST_LENGTH_MASK,
+ info));
+
+ if (info & inc)
+ xi |= BCM2711_DMA40_INC;
+ if (info & width)
+ xi |= BCM2711_DMA40_SIZE_128;
+ if (info & ignore)
+ xi |= BCM2711_DMA40_IGNORE;
+
+ return xi;
+}
+
+/* Control block pointers of extended channels are 32 byte units */
+static u32 bcm2835_dma_cb_ptr(struct bcm2835_chan *c, dma_addr_t paddr)
+{
+ return c->ext_addr ? paddr >> 5 : paddr;
+}
+
static void bcm2835_dma_cb_set_src(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e, dma_addr_t addr)
{
- e->cb->src = addr;
+ if (c->is_40bit_channel) {
+ e->cb40->src = lower_32_bits(addr);
+ FIELD_MODIFY(BCM2711_DMA40_ADDR_HI, &e->cb40->srci,
+ upper_32_bits(addr));
+ return;
+ }
+
+ e->cb->src = lower_32_bits(addr);
}
static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e, dma_addr_t addr)
{
- e->cb->dst = addr;
+ if (c->is_40bit_channel) {
+ e->cb40->dst = lower_32_bits(addr);
+ FIELD_MODIFY(BCM2711_DMA40_ADDR_HI, &e->cb40->dsti,
+ upper_32_bits(addr));
+ return;
+ }
+
+ e->cb->dst = lower_32_bits(addr);
}
static void bcm2835_dma_cb_init(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e, u32 info,
dma_addr_t src, dma_addr_t dst)
{
- struct bcm2835_dma_cb *cb = e->cb;
+ if (c->is_40bit_channel) {
+ struct bcm2711_dma40_cb *cb = e->cb40;
+
+ cb->ti = bcm2711_dma40_ti(info);
+ cb->srci = bcm2711_dma40_xi(info, BCM2835_DMA_S_INC,
+ BCM2835_DMA_S_WIDTH,
+ BCM2835_DMA_S_IGNORE);
+ cb->dsti = bcm2711_dma40_xi(info, BCM2835_DMA_D_INC,
+ BCM2835_DMA_D_WIDTH,
+ BCM2835_DMA_D_IGNORE);
+ cb->len = 0;
+ cb->next_cb = 0;
+ cb->rsvd = 0;
+ } else {
+ struct bcm2835_dma_cb *cb = e->cb;
- cb->info = info;
- cb->length = 0;
- cb->stride = 0;
- cb->next = 0;
+ cb->info = info;
+ cb->length = 0;
+ cb->stride = 0;
+ cb->next = 0;
+ }
bcm2835_dma_cb_set_src(c, e, src);
bcm2835_dma_cb_set_dst(c, e, dst);
@@ -247,26 +397,40 @@ static void bcm2835_dma_cb_init(struct bcm2835_chan *c,
static u32 bcm2835_dma_cb_get_len(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e)
{
- return e->cb->length;
+ return c->is_40bit_channel ? e->cb40->len : e->cb->length;
}
static void bcm2835_dma_cb_set_len(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e, u32 len)
{
- e->cb->length = len;
+ if (c->is_40bit_channel)
+ e->cb40->len = len;
+ else
+ e->cb->length = len;
}
static void bcm2835_dma_cb_add_info(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e, u32 info)
{
- e->cb->info |= info;
+ if (c->is_40bit_channel)
+ e->cb40->ti |= bcm2711_dma40_ti(info);
+ else
+ e->cb->info |= info;
}
static void bcm2835_dma_cb_set_next(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e,
dma_addr_t next)
{
- e->cb->next = next;
+ if (c->is_40bit_channel)
+ e->cb40->next_cb = bcm2835_dma_cb_ptr(c, next);
+ else
+ e->cb->next = bcm2835_dma_cb_ptr(c, next);
+}
+
+static dma_addr_t bcm2711_dma40_addr(u32 lo, u32 xi)
+{
+ return (u64)FIELD_GET(BCM2711_DMA40_ADDR_HI, xi) << 32 | lo;
}
/* Memory side address of a control block */
@@ -274,6 +438,12 @@ static dma_addr_t bcm2835_dma_cb_mem_addr(struct bcm2835_chan *c,
struct bcm2835_cb_entry *e,
enum dma_transfer_direction dir)
{
+ if (c->is_40bit_channel) {
+ if (dir == DMA_DEV_TO_MEM)
+ return bcm2711_dma40_addr(e->cb40->dst, e->cb40->dsti);
+ return bcm2711_dma40_addr(e->cb40->src, e->cb40->srci);
+ }
+
return dir == DMA_DEV_TO_MEM ? e->cb->dst : e->cb->src;
}
@@ -282,11 +452,32 @@ static dma_addr_t bcm2835_dma_get_pos(struct bcm2835_chan *c,
{
void __iomem *base = c->chan_base;
+ if (c->is_40bit_channel) {
+ if (dir == DMA_DEV_TO_MEM)
+ return bcm2711_dma40_addr(readl(base + BCM2711_DMA40_DEST),
+ readl(base + BCM2711_DMA40_DESTI));
+ return bcm2711_dma40_addr(readl(base + BCM2711_DMA40_SRC),
+ readl(base + BCM2711_DMA40_SRCI));
+ }
+
if (dir == DMA_DEV_TO_MEM)
return readl(base + BCM2835_DMA_DEST_AD);
return readl(base + BCM2835_DMA_SOURCE_AD);
}
+/*
+ * Clients describe peripheral FIFOs by their legacy bus address. Channels
+ * with extended addressing see that window at a SoC specific offset.
+ */
+static dma_addr_t bcm2835_dma_periph_addr(struct bcm2835_chan *c,
+ dma_addr_t addr)
+{
+ if (addr >= BCM2835_DMA_PERIPH_BASE && addr < BCM2835_DMA_PERIPH_END)
+ addr += c->periph_offset;
+
+ return addr;
+}
+
static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
{
size_t i;
@@ -475,6 +666,27 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
}
}
+static void bcm2711_dma40_abort(struct bcm2835_chan *c)
+{
+ void __iomem *chan_base = c->chan_base;
+ long timeout = 10000;
+
+ writel(readl(chan_base + BCM2711_DMA40_CS) & ~BCM2711_DMA40_ACTIVE,
+ chan_base + BCM2711_DMA40_CS);
+
+ while ((readl(chan_base + BCM2711_DMA40_CS) &
+ BCM2711_DMA40_TRANSACTIONS) && --timeout)
+ cpu_relax();
+
+ if (!timeout)
+ dev_err(c->vc.chan.device->dev,
+ "failed to complete outstanding transactions\n");
+
+ writel(BCM2711_DMA40_PROT, chan_base + BCM2711_DMA40_CS);
+ writel(readl(chan_base + BCM2711_DMA40_DEBUG) | BCM2711_DMA40_DEBUG_RESET,
+ chan_base + BCM2711_DMA40_DEBUG);
+}
+
static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
@@ -483,10 +695,16 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
/*
* A zero control block address means the channel is idle.
* (The ACTIVE flag in the CS register is not a reliable indicator.)
+ * The CB register of 40-bit channels is at the same offset.
*/
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;
+ if (c->is_40bit_channel) {
+ bcm2711_dma40_abort(c);
+ return;
+ }
+
/* Write 0 to the active bit - Pause the DMA */
writel(0, chan_base + BCM2835_DMA_CS);
@@ -517,8 +735,13 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
c->desc = d = to_bcm2835_dma_desc(&vd->tx);
- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
- writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+ writel(bcm2835_dma_cb_ptr(c, d->cb_list[0].paddr),
+ c->chan_base + BCM2835_DMA_ADDR);
+ if (c->is_40bit_channel)
+ writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT,
+ c->chan_base + BCM2711_DMA40_CS);
+ else
+ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
}
static irqreturn_t bcm2835_dma_callback(int irq, void *data)
@@ -545,8 +768,12 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
* if this IRQ handler is threaded.) If the channel is finished, it
* will remain idle despite the ACTIVE flag being set.
*/
- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
- c->chan_base + BCM2835_DMA_CS);
+ if (c->is_40bit_channel)
+ writel(BCM2711_DMA40_INT | BCM2711_DMA40_ACTIVE |
+ BCM2711_DMA40_PROT, c->chan_base + BCM2711_DMA40_CS);
+ else
+ writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+ c->chan_base + BCM2835_DMA_CS);
d = c->desc;
@@ -723,12 +950,12 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
if (direction == DMA_DEV_TO_MEM) {
if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- src = c->cfg.src_addr;
+ src = bcm2835_dma_periph_addr(c, c->cfg.src_addr);
info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- dst = c->cfg.dst_addr;
+ dst = bcm2835_dma_periph_addr(c, c->cfg.dst_addr);
info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
}
@@ -797,13 +1024,13 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
if (direction == DMA_DEV_TO_MEM) {
if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- src = c->cfg.src_addr;
+ src = bcm2835_dma_periph_addr(c, c->cfg.src_addr);
dst = buf_addr;
info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
} else {
if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
return NULL;
- dst = c->cfg.dst_addr;
+ dst = bcm2835_dma_periph_addr(c, c->cfg.dst_addr);
src = buf_addr;
info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
@@ -893,11 +1120,17 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
c->irq_number = irq;
c->irq_flags = irq_flags;
+ if (d->cfg->chan_40bit_mask & BIT(chan_id))
+ c->is_40bit_channel = true;
/* check in DEBUG register if this is a LITE channel */
- if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
- BCM2835_DMA_DEBUG_LITE)
+ else if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
+ BCM2835_DMA_DEBUG_LITE)
c->is_lite_channel = true;
+ c->ext_addr = c->is_40bit_channel;
+ if (c->ext_addr)
+ c->periph_offset = d->cfg->periph_offset;
+
return 0;
}
@@ -917,6 +1150,7 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
static const struct of_device_id bcm2835_dma_of_match[] = {
{ .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
+ { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (3 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 4/9] dmaengine: bcm2835: Add BCM2711 40-bit channel support Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:25 ` sashiko-bot
2026-11-02 9:06 ` [PATCH 6/9] ARM: dts: bcm2711: Add 40-bit DMA controller Peter Robinson
` (3 subsequent siblings)
8 siblings, 1 reply; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
BCM2712 legacy layout channels carry address bits [39:32] in the
STRIDE field and use 32-byte units for control block pointers.
Channels 6-11 are DMA4 engines. Peripherals are seen at 0x10_7c000000.
Add the brcm,bcm2712-dma compatible with a 40-bit DMA mask.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
drivers/dma/bcm2835-dma.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index 35f8c47dea89f..f06609b04a098 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -44,11 +44,14 @@
* @chan_40bit_mask: channels implementing the BCM2711 40-bit register layout
* @periph_offset: offset at which channels with extended addressing see the
* legacy 0x7c000000 - 0x7fffffff peripheral window
+ * @ext_addr: legacy layout channels carry address bits [39:32] in the STRIDE
+ * field and control block pointers are stored right-shifted by 5
*/
struct bcm2835_dma_cfg {
u64 dma_mask;
u32 chan_40bit_mask;
u64 periph_offset;
+ bool ext_addr;
};
/**
@@ -212,6 +215,10 @@ struct bcm2835_desc {
#define MAX_DMA_LEN SZ_1G
#define MAX_LITE_DMA_LEN (SZ_64K - 4)
+/* BCM2712 legacy layout channels: address bits [39:32] in the STRIDE field */
+#define BCM2712_DMA_STRIDE_SRC_HI GENMASK(7, 0)
+#define BCM2712_DMA_STRIDE_DST_HI GENMASK(15, 8)
+
/* Legacy peripheral bus window used by DMA clients for FIFO addresses */
#define BCM2835_DMA_PERIPH_BASE 0x7c000000
#define BCM2835_DMA_PERIPH_END 0x80000000
@@ -271,6 +278,13 @@ static const struct bcm2835_dma_cfg bcm2711_dma_cfg = {
.periph_offset = 0x400000000ULL,
};
+static const struct bcm2835_dma_cfg bcm2712_dma_cfg = {
+ .dma_mask = DMA_BIT_MASK(40),
+ .chan_40bit_mask = GENMASK(11, 6),
+ .periph_offset = 0x1000000000ULL,
+ .ext_addr = true,
+};
+
static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
{
/* lite and normal channels have different max frame length */
@@ -349,6 +363,9 @@ static void bcm2835_dma_cb_set_src(struct bcm2835_chan *c,
}
e->cb->src = lower_32_bits(addr);
+ if (c->ext_addr)
+ FIELD_MODIFY(BCM2712_DMA_STRIDE_SRC_HI, &e->cb->stride,
+ upper_32_bits(addr));
}
static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
@@ -362,6 +379,9 @@ static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
}
e->cb->dst = lower_32_bits(addr);
+ if (c->ext_addr)
+ FIELD_MODIFY(BCM2712_DMA_STRIDE_DST_HI, &e->cb->stride,
+ upper_32_bits(addr));
}
static void bcm2835_dma_cb_init(struct bcm2835_chan *c,
@@ -1127,7 +1147,7 @@ static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
BCM2835_DMA_DEBUG_LITE)
c->is_lite_channel = true;
- c->ext_addr = c->is_40bit_channel;
+ c->ext_addr = c->is_40bit_channel || d->cfg->ext_addr;
if (c->ext_addr)
c->periph_offset = d->cfg->periph_offset;
@@ -1151,6 +1171,7 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od)
static const struct of_device_id bcm2835_dma_of_match[] = {
{ .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
{ .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
+ { .compatible = "brcm,bcm2712-dma", .data = &bcm2712_dma_cfg },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 6/9] ARM: dts: bcm2711: Add 40-bit DMA controller
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (4 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:06 ` [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio Peter Robinson
` (2 subsequent siblings)
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Describe the four DMA4 channels (11-14) of BCM2711. They sit
on the SCB so they can reach all of memory.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
arch/arm/boot/dts/broadcom/bcm2711.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/boot/dts/broadcom/bcm2711.dtsi b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
index 5e3b4bb393967..9d70bea17b11c 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
@@ -602,6 +602,22 @@ genet_mdio: mdio@e14 {
};
};
+ dma40: dma-controller@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
+ reg = <0x0 0x7e007b00 0x400>;
+ interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
+ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, /* dma4 13 */
+ <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; /* dma4 14 */
+ interrupt-names = "dma11",
+ "dma12",
+ "dma13",
+ "dma14";
+ #dma-cells = <1>;
+ brcm,dma-channel-mask = <0x7800>;
+ status = "disabled";
+ };
+
xhci: usb@7e9c0000 {
compatible = "brcm,bcm2711-xhci", "brcm,xhci-brcm-v2";
reg = <0x0 0x7e9c0000 0x100000>;
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (5 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 6/9] ARM: dts: bcm2711: Add 40-bit DMA controller Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:22 ` sashiko-bot
2026-11-02 9:06 ` [PATCH 8/9] arm64: dts: broadcom: bcm2712: Add DMA controllers Peter Robinson
2026-11-02 9:06 ` [PATCH 9/9] arm64: dts: broadcom: bcm2712-rpi-5-b: Enable HDMI audio Peter Robinson
8 siblings, 1 reply; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Enable the dma40 controller on the Raspberry Pi 4, ch 11
is reserved for the VPU firmware for VCHIQ communication,
and add the DMA channels for HDMI audio.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
index 1eb6406449d19..a49a732d3f64e 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
@@ -42,6 +42,12 @@ i2c0_1: i2c@1 {
};
};
+&dma40 {
+ /* The VPU firmware uses DMA channel 11 for VCHIQ */
+ brcm,dma-channel-mask = <0x7000>;
+ status = "okay";
+};
+
&firmware {
expgpio: gpio {
compatible = "raspberrypi,firmware-gpio";
@@ -59,12 +65,14 @@ reset: reset {
&hdmi0 {
clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
clock-names = "hdmi", "bvb", "audio", "cec";
+ dmas = <&dma40 10>;
wifi-2.4ghz-coexistence;
};
&hdmi1 {
clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
clock-names = "hdmi", "bvb", "audio", "cec";
+ dmas = <&dma40 17>;
wifi-2.4ghz-coexistence;
};
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 8/9] arm64: dts: broadcom: bcm2712: Add DMA controllers
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (6 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
2026-11-02 9:06 ` [PATCH 9/9] arm64: dts: broadcom: bcm2712-rpi-5-b: Enable HDMI audio Peter Robinson
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Describe the BCM2712 legacy DMA channels (0-5) and the DMA4
channels (6-11) as separate nodes, both disabled for boards
to enable. Node labels follow the Raspberry Pi downstream tree.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 40 +++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
index edae2f3584a8d..be4e94c9e7695 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
@@ -546,6 +546,46 @@ axi: axi {
<0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>,
<0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>;
+ dma32: dma-controller@1000010000 {
+ compatible = "brcm,bcm2712-dma";
+ reg = <0x10 0x00010000 0x0 0x600>;
+ interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma0",
+ "dma1",
+ "dma2",
+ "dma3",
+ "dma4",
+ "dma5";
+ #dma-cells = <1>;
+ brcm,dma-channel-mask = <0x0035>;
+ status = "disabled";
+ };
+
+ dma40: dma-controller@1000010600 {
+ compatible = "brcm,bcm2712-dma";
+ reg = <0x10 0x00010600 0x0 0x600>;
+ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, /* dma4 6 */
+ <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, /* dma4 7 */
+ <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, /* dma4 8 */
+ <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 9 */
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 10 */
+ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; /* dma4 11 */
+ interrupt-names = "dma6",
+ "dma7",
+ "dma8",
+ "dma9",
+ "dma10",
+ "dma11";
+ #dma-cells = <1>;
+ brcm,dma-channel-mask = <0x0fc0>;
+ status = "disabled";
+ };
+
pcie0: pcie@1000100000 {
compatible = "brcm,bcm2712-pcie";
reg = <0x10 0x00100000 0x00 0x9310>;
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 9/9] arm64: dts: broadcom: bcm2712-rpi-5-b: Enable HDMI audio
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
` (7 preceding siblings ...)
2026-11-02 9:06 ` [PATCH 8/9] arm64: dts: broadcom: bcm2712: Add DMA controllers Peter Robinson
@ 2026-11-02 9:06 ` Peter Robinson
8 siblings, 0 replies; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 9:06 UTC (permalink / raw)
To: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list
Cc: Peter Robinson
Enable the DMA4 controller, excluding channel 11 which the VPU
firmware uses for VCHIQ, and hook up HDMI audio to it. The D0
stepping uses different DREQ lines for the HDMI controllers.
DREQ numbers and channel mask match Raspberry Pi downstream.
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
---
arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts | 8 ++++++++
arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi | 10 ++++++++++
2 files changed, 18 insertions(+)
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts
index ae4b7c587dbc5..3322862906f62 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-d-rpi-5-b.dts
@@ -51,6 +51,14 @@ &pinctrl_aon {
reg = <0x7d510700 0x1c>;
};
+&hdmi0 {
+ dmas = <&dma40 12>;
+};
+
+&hdmi1 {
+ dmas = <&dma40 13>;
+};
+
&uart10 {
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
};
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi
index e5030da60f9e7..3b55840414975 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b-base.dtsi
@@ -117,6 +117,12 @@ &disp_intr {
status = "okay";
};
+&dma40 {
+ /* The VPU firmware uses DMA channel 11 for VCHIQ */
+ brcm,dma-channel-mask = <0x07c0>;
+ status = "okay";
+};
+
&pinctrl {
bt_shutdown_default: bt-shutdown-default-state {
function = "gpio";
@@ -254,12 +260,16 @@ &hvs {
&hdmi0 {
clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
clock-names = "hdmi", "bvb", "audio", "cec";
+ dmas = <&dma40 10>;
+ dma-names = "audio-rx";
status = "okay";
};
&hdmi1 {
clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
clock-names = "hdmi", "bvb", "audio", "cec";
+ dmas = <&dma40 17>;
+ dma-names = "audio-rx";
status = "okay";
};
--
2.56.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio
2026-11-02 9:06 ` [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio Peter Robinson
@ 2026-11-02 9:22 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-11-02 9:22 UTC (permalink / raw)
To: Peter Robinson, dmaengine, devicetree; +Cc: robh, conor+dt, vkoul, Frank.Li
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] ARM: dts: bcm2711: hdmi audio dma40 truncates FIFO address on !LPAE
--
commit 849f01a77c8d05d1bc02c231948e58143158b712
Author: Peter Robinson <pbrobinson@gmail.com>
ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio
This commit enables the 40-bit DMA controller (dma40) on the Raspberry Pi 4
and switches hdmi0 and hdmi1 audio DMA to use dma40 channels 10 and 17.
> diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
> index 1eb6406449d19..a49a732d3f64e 100644
> --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
> +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
> @@ -42,6 +42,12 @@ i2c0_1: i2c@1 {
> };
> };
>
> +&dma40 {
> + /* The VPU firmware uses DMA channel 11 for VCHIQ */
> + brcm,dma-channel-mask = <0x7000>;
> + status = "okay";
> +};
> +
> &firmware {
> expgpio: gpio {
> compatible = "raspberrypi,firmware-gpio";
> @@ -59,12 +65,14 @@ reset: reset {
> &hdmi0 {
> clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
> clock-names = "hdmi", "bvb", "audio", "cec";
> + dmas = <&dma40 10>;
> wifi-2.4ghz-coexistence;
> };
>
> &hdmi1 {
> clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
> clock-names = "hdmi", "bvb", "audio", "cec";
> + dmas = <&dma40 17>;
[Severity: Critical]
Will switching hdmi0 and hdmi1 in arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
to dma40 cause a regression on 32-bit ARM kernels built without
CONFIG_ARM_LPAE (such as bcm2835_defconfig or multi_v7_defconfig)?
When playing HDMI audio, vc4_hdmi_audio_init() configures
dma_slave_config.dst_addr with the VPU bus address of the MAI_DATA FIFO
(0x7ef200xx). With hdmi0 and hdmi1 moved to dma40,
bcm2835_dma_prep_dma_cyclic() calls bcm2835_dma_periph_addr() to translate the
0x7c000000..0x7fffffff peripheral window to the 35-bit SCB bus window
(0x4_7c000000..0x4_7fffffff) by adding c->periph_offset (0x400000000ULL):
drivers/dma/bcm2835-dma.c:bcm2835_dma_periph_addr() {
if (addr >= BCM2835_DMA_PERIPH_BASE && addr < BCM2835_DMA_PERIPH_END)
addr += c->periph_offset;
return addr;
}
However, CONFIG_ARCH_BCM2835 does not select CONFIG_ARM_LPAE or
CONFIG_ARCH_DMA_ADDR_T_64BIT, so dma_addr_t is a 32-bit u32 on non-LPAE 32-bit
ARM builds. Because bcm2835_dma_periph_addr() takes and returns dma_addr_t
(and dst in bcm2835_dma_prep_dma_cyclic(), bcm2835_dma_prep_slave_sg(),
bcm2835_dma_create_cb_chain(), bcm2835_dma_cb_init(), and
bcm2835_dma_cb_set_dst() is also typed as dma_addr_t rather than u64), adding
0x400000000ULL truncates the upper 32 bits, leaving addr as 0x7ef200xx.
When bcm2835_dma_cb_set_dst() then programs the 40-bit control block:
drivers/dma/bcm2835-dma.c:bcm2835_dma_cb_set_dst() {
if (c->is_40bit_channel) {
e->cb40->dst = lower_32_bits(addr);
FIELD_MODIFY(BCM2711_DMA40_ADDR_HI, &e->cb40->dsti,
upper_32_bits(addr));
return;
}
upper_32_bits(addr) evaluates to 0 instead of 0x4. Doesn't this program the
DMA40 engine to write HDMI audio samples to SCB address 0x0_7ef200xx (physical
SDRAM around the 2 GiB mark) instead of the HDMI MAI FIFO at 0x4_7ef200xx,
breaking HDMI audio and corrupting system memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102090713.896324-1-pbrobinson@gmail.com?part=7
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support
2026-11-02 9:06 ` [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support Peter Robinson
@ 2026-11-02 9:25 ` sashiko-bot
0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-11-02 9:25 UTC (permalink / raw)
To: Peter Robinson, dmaengine, devicetree; +Cc: robh, conor+dt, vkoul, Frank.Li
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] dmaengine: bcm2835: missing STRIDE high bits in residue address helpers
--
commit 036b8292dd25c0046b851159c80cd9142c283fa1
Author: Peter Robinson <pbrobinson@gmail.com>
dmaengine: bcm2835: Add BCM2712 support
This commit adds support for the BCM2712 DMA controller with a 40-bit DMA
mask, configuring channels 6-11 as 40-bit DMA4 engines and setting
ext_addr for legacy layout channels so that address bits [39:32] are
stored in the control block STRIDE field.
> diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
> index 35f8c47dea89f..f06609b04a098 100644
> --- a/drivers/dma/bcm2835-dma.c
> +++ b/drivers/dma/bcm2835-dma.c
[ ... ]
> @@ -349,6 +363,9 @@ static void bcm2835_dma_cb_set_src(struct bcm2835_chan *c,
> }
>
> e->cb->src = lower_32_bits(addr);
> + if (c->ext_addr)
> + FIELD_MODIFY(BCM2712_DMA_STRIDE_SRC_HI, &e->cb->stride,
> + upper_32_bits(addr));
> }
>
> static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
> @@ -362,6 +379,9 @@ static void bcm2835_dma_cb_set_dst(struct bcm2835_chan *c,
> }
>
> e->cb->dst = lower_32_bits(addr);
> + if (c->ext_addr)
> + FIELD_MODIFY(BCM2712_DMA_STRIDE_DST_HI, &e->cb->stride,
> + upper_32_bits(addr));
> }
[Severity: Medium]
Should bcm2835_dma_cb_mem_addr() and bcm2835_dma_get_pos() also account for
the upper address bits [39:32] stored in the STRIDE field when c->ext_addr
is set on legacy layout channels?
In drivers/dma/bcm2835-dma.c, bcm2835_dma_cb_set_src() and
bcm2835_dma_cb_set_dst() place upper_32_bits(addr) into e->cb->stride under
BCM2712_DMA_STRIDE_SRC_HI and BCM2712_DMA_STRIDE_DST_HI when c->ext_addr is
true on non-40-bit channels (such as channels 0-5 on BCM2712).
However, bcm2835_dma_cb_mem_addr() and bcm2835_dma_get_pos() only check
c->is_40bit_channel and otherwise return only the lower 32 bits:
drivers/dma/bcm2835-dma.c:bcm2835_dma_cb_mem_addr() {
if (c->is_40bit_channel) {
if (dir == DMA_DEV_TO_MEM)
return bcm2711_dma40_addr(e->cb40->dst, e->cb40->dsti);
return bcm2711_dma40_addr(e->cb40->src, e->cb40->srci);
}
return dir == DMA_DEV_TO_MEM ? e->cb->dst : e->cb->src;
}
drivers/dma/bcm2835-dma.c:bcm2835_dma_get_pos() {
...
if (dir == DMA_DEV_TO_MEM)
return readl(base + BCM2835_DMA_DEST_AD);
return readl(base + BCM2835_DMA_SOURCE_AD);
}
When bcm2835_dma_tx_status() calls bcm2835_dma_get_pos() and
bcm2835_dma_desc_size_pos() to compute transfer residue:
drivers/dma/bcm2835-dma.c:bcm2835_dma_desc_size_pos() {
for (size = i = 0; i < d->frames; i++) {
struct bcm2835_cb_entry *e = &d->cb_list[i];
size_t this_size = bcm2835_dma_cb_get_len(d->c, e);
dma_addr_t dma = bcm2835_dma_cb_mem_addr(d->c, e, d->dir);
if (size)
size += this_size;
else if (addr >= dma && addr < dma + this_size)
size += dma + this_size - addr;
}
return size;
}
If a transfer on a BCM2712 legacy layout channel crosses a 4 GiB boundary,
dma + this_size exceeds 0xffffffff while addr wraps in the 32-bit
BCM2835_DMA_SOURCE_AD or BCM2835_DMA_DEST_AD register, causing the range
check (addr >= dma && addr < dma + this_size) to fail and report zero
residue while the transfer is still running. Similarly, if scatter-gather
segments in different 4 GiB regions have overlapping low 32-bit addresses,
bcm2835_dma_desc_size_pos() can match the wrong control block.
Could bcm2835_dma_cb_mem_addr() and bcm2835_dma_get_pos() reconstruct the
full 40-bit address from the STRIDE field when c->ext_addr is true?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102090713.896324-1-pbrobinson@gmail.com?part=5
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712
2026-11-02 9:06 ` [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712 Peter Robinson
@ 2026-11-02 19:39 ` Conor Dooley
2026-11-02 20:18 ` Peter Robinson
0 siblings, 1 reply; 15+ messages in thread
From: Conor Dooley @ 2026-11-02 19:39 UTC (permalink / raw)
To: Peter Robinson
Cc: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
[-- Attachment #1: Type: text/plain, Size: 3041 bytes --]
On Mon, Nov 02, 2026 at 09:06:51AM +0000, Peter Robinson wrote:
> Add compatibles for the BCM2711 40-bit DMA4 channels, which are
> described by a separate node covering channels 11-14, and for the
> BCM2712 DMA controllers. The register address of a node identifies its
> first channel, allowing a controller to be split across nodes.
>
> Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Conor Dooley <conor+dt@kernel.org>
>
> ---
> .../bindings/dma/brcm,bcm2835-dma.yaml | 31 +++++++++++++++++--
> 1 file changed, 29 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> index c9b9a5490826c..31d97a10a0ea9 100644
> --- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> +++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> @@ -9,18 +9,27 @@ title: BCM2835 DMA controller
> maintainers:
> - Nicolas Saenz Julienne <nsaenz@kernel.org>
>
> -description:
> +description: |
> The BCM2835 DMA controller has 16 channels in total. Only the lower
> 13 channels have an associated IRQ. Some arbitrary channels are used by the
> VideoCore firmware (1,3,6,7 in the current firmware version). The channels
> 0, 2 and 3 have special functionality and should not be used by the driver.
>
> + BCM2711 channels 11 to 14 are 40-bit capable DMA4 channels, described by a
> + separate brcm,bcm2711-dma node. BCM2712 has 40-bit addressing on all
> + channels, with channels 6 to 11 being DMA4 channels. A controller may be
> + split across several nodes, the register address identifies the first
> + channel of a node.
Uh no, a device should not be split across different nodes. This should
not be needed, why can't your driver handle the upper channels having
different capabilities?
Thanks,
Conor.
> +
> allOf:
> - $ref: dma-controller.yaml#
>
> properties:
> compatible:
> - const: brcm,bcm2835-dma
> + enum:
> + - brcm,bcm2835-dma
> + - brcm,bcm2711-dma
> + - brcm,bcm2712-dma
>
> reg:
> maxItems: 1
> @@ -99,4 +108,22 @@ examples:
> brcm,dma-channel-mask = <0x7f35>;
> };
>
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> + dma-controller@7e007b00 {
> + compatible = "brcm,bcm2711-dma";
> + reg = <0x7e007b00 0x400>;
> + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "dma11",
> + "dma12",
> + "dma13",
> + "dma14";
> + #dma-cells = <1>;
> + brcm,dma-channel-mask = <0x7000>;
> + };
> +
> ...
> --
> 2.56.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712
2026-11-02 19:39 ` Conor Dooley
@ 2026-11-02 20:18 ` Peter Robinson
2026-11-02 20:38 ` Florian Fainelli
0 siblings, 1 reply; 15+ messages in thread
From: Peter Robinson @ 2026-11-02 20:18 UTC (permalink / raw)
To: Conor Dooley
Cc: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
On Mon, 2 Nov 2026 at 19:39, Conor Dooley <conor@kernel.org> wrote:
>
> On Mon, Nov 02, 2026 at 09:06:51AM +0000, Peter Robinson wrote:
> > Add compatibles for the BCM2711 40-bit DMA4 channels, which are
> > described by a separate node covering channels 11-14, and for the
> > BCM2712 DMA controllers. The register address of a node identifies its
> > first channel, allowing a controller to be split across nodes.
> >
> > Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> >
> > ---
> > .../bindings/dma/brcm,bcm2835-dma.yaml | 31 +++++++++++++++++--
> > 1 file changed, 29 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> > index c9b9a5490826c..31d97a10a0ea9 100644
> > --- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> > +++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
> > @@ -9,18 +9,27 @@ title: BCM2835 DMA controller
> > maintainers:
> > - Nicolas Saenz Julienne <nsaenz@kernel.org>
> >
> > -description:
> > +description: |
> > The BCM2835 DMA controller has 16 channels in total. Only the lower
> > 13 channels have an associated IRQ. Some arbitrary channels are used by the
> > VideoCore firmware (1,3,6,7 in the current firmware version). The channels
> > 0, 2 and 3 have special functionality and should not be used by the driver.
> >
> > + BCM2711 channels 11 to 14 are 40-bit capable DMA4 channels, described by a
> > + separate brcm,bcm2711-dma node. BCM2712 has 40-bit addressing on all
> > + channels, with channels 6 to 11 being DMA4 channels. A controller may be
> > + split across several nodes, the register address identifies the first
> > + channel of a node.
>
> Uh no, a device should not be split across different nodes. This should
> not be needed, why can't your driver handle the upper channels having
> different capabilities?
This is the way it's done in the downstream kernel, unfortunately
Broadcom doesn't publish the HW details so either they'd need to
contribute the support (which they've not done since the RPi4 was
available) or I'm not sure what the other options would be. Open to
suggestions.
> Thanks,
> Conor.
>
> > +
> > allOf:
> > - $ref: dma-controller.yaml#
> >
> > properties:
> > compatible:
> > - const: brcm,bcm2835-dma
> > + enum:
> > + - brcm,bcm2835-dma
> > + - brcm,bcm2711-dma
> > + - brcm,bcm2712-dma
> >
> > reg:
> > maxItems: 1
> > @@ -99,4 +108,22 @@ examples:
> > brcm,dma-channel-mask = <0x7f35>;
> > };
> >
> > + - |
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > + dma-controller@7e007b00 {
> > + compatible = "brcm,bcm2711-dma";
> > + reg = <0x7e007b00 0x400>;
> > + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>,
> > + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>,
> > + <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
> > + <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
> > + interrupt-names = "dma11",
> > + "dma12",
> > + "dma13",
> > + "dma14";
> > + #dma-cells = <1>;
> > + brcm,dma-channel-mask = <0x7000>;
> > + };
> > +
> > ...
> > --
> > 2.56.0
> >
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712
2026-11-02 20:18 ` Peter Robinson
@ 2026-11-02 20:38 ` Florian Fainelli
0 siblings, 0 replies; 15+ messages in thread
From: Florian Fainelli @ 2026-11-02 20:38 UTC (permalink / raw)
To: Peter Robinson, Conor Dooley
Cc: Florian Fainelli, Dave Stevenson, Ray Jui, Scott Branden,
Vinod Koul, Frank Li, dmaengine, devicetree, linux-rpi-kernel,
linux-arm-kernel, bcm-kernel-feedback-list, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
On 11/2/26 12:18, Peter Robinson wrote:
> On Mon, 2 Nov 2026 at 19:39, Conor Dooley <conor@kernel.org> wrote:
>>
>> On Mon, Nov 02, 2026 at 09:06:51AM +0000, Peter Robinson wrote:
>>> Add compatibles for the BCM2711 40-bit DMA4 channels, which are
>>> described by a separate node covering channels 11-14, and for the
>>> BCM2712 DMA controllers. The register address of a node identifies its
>>> first channel, allowing a controller to be split across nodes.
>>>
>>> Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
>>> Cc: Rob Herring <robh@kernel.org>
>>> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
>>> Cc: Conor Dooley <conor+dt@kernel.org>
>>>
>>> ---
>>> .../bindings/dma/brcm,bcm2835-dma.yaml | 31 +++++++++++++++++--
>>> 1 file changed, 29 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
>>> index c9b9a5490826c..31d97a10a0ea9 100644
>>> --- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
>>> +++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml
>>> @@ -9,18 +9,27 @@ title: BCM2835 DMA controller
>>> maintainers:
>>> - Nicolas Saenz Julienne <nsaenz@kernel.org>
>>>
>>> -description:
>>> +description: |
>>> The BCM2835 DMA controller has 16 channels in total. Only the lower
>>> 13 channels have an associated IRQ. Some arbitrary channels are used by the
>>> VideoCore firmware (1,3,6,7 in the current firmware version). The channels
>>> 0, 2 and 3 have special functionality and should not be used by the driver.
>>>
>>> + BCM2711 channels 11 to 14 are 40-bit capable DMA4 channels, described by a
>>> + separate brcm,bcm2711-dma node. BCM2712 has 40-bit addressing on all
>>> + channels, with channels 6 to 11 being DMA4 channels. A controller may be
>>> + split across several nodes, the register address identifies the first
>>> + channel of a node.
>>
>> Uh no, a device should not be split across different nodes. This should
>> not be needed, why can't your driver handle the upper channels having
>> different capabilities?
>
> This is the way it's done in the downstream kernel, unfortunately
> Broadcom doesn't publish the HW details so either they'd need to
> contribute the support (which they've not done since the RPi4 was
> available) or I'm not sure what the other options would be. Open to
> suggestions.
Neither here nor there, IMHO, yes it would be nice if documentation was
published (and a customer like Raspberry Pi is probably one of the few
that can get the needle to move there).
You have the bcm2711 and bcm2712 compatible strings which should be
enough for you to know which channels are 40-bit capable and which are
not, yet keep the same Device Tree node.
--
Florian
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-11-02 20:38 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 9:06 [PATCH 0/9] dma: Add BCM2711 and BCM2712 support to bcm2835-dma Peter Robinson
2026-11-02 9:06 ` [PATCH 1/9] dt-bindings: dma: brcm,bcm2835-dma: Add BCM2711 and BCM2712 Peter Robinson
2026-11-02 19:39 ` Conor Dooley
2026-11-02 20:18 ` Peter Robinson
2026-11-02 20:38 ` Florian Fainelli
2026-11-02 9:06 ` [PATCH 2/9] dmaengine: bcm2835: Refactor control block handling Peter Robinson
2026-11-02 9:06 ` [PATCH 3/9] dmaengine: bcm2835: Support controllers split across several nodes Peter Robinson
2026-11-02 9:06 ` [PATCH 4/9] dmaengine: bcm2835: Add BCM2711 40-bit channel support Peter Robinson
2026-11-02 9:06 ` [PATCH 5/9] dmaengine: bcm2835: Add BCM2712 support Peter Robinson
2026-11-02 9:25 ` sashiko-bot
2026-11-02 9:06 ` [PATCH 6/9] ARM: dts: bcm2711: Add 40-bit DMA controller Peter Robinson
2026-11-02 9:06 ` [PATCH 7/9] ARM: dts: bcm2711-rpi: Use 40-bit DMA for HDMI audio Peter Robinson
2026-11-02 9:22 ` sashiko-bot
2026-11-02 9:06 ` [PATCH 8/9] arm64: dts: broadcom: bcm2712: Add DMA controllers Peter Robinson
2026-11-02 9:06 ` [PATCH 9/9] arm64: dts: broadcom: bcm2712-rpi-5-b: Enable HDMI audio Peter Robinson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox