Skip to contentSkip to conversation

drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU)

RFC PATCH Superseded Leander Kieweg 5 participants 17 messages Jul 14 – Jul 21

Subsystems (3)

v1 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

cover drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU)

Hi everyone,

This series adds a DRM driver for GlandaGPU, a custom open-source 2D graphics core I designed in VHDL. It currently runs on a Cyclone V SoC (Terasic DE10-Standard), with VGA output at 640x480@60Hz. I also built a QEMU fork that emulates the same MMIO/VRAM interface as a digital twin, so I can develop and test without needing the FPGA board.

  Hardware/VHDL:    https://github.com/stiangglanda/GlandaGPU
  QEMU fork:        https://github.com/stiangglanda/qemu-glandagpu
  Userspace tests:  https://github.com/stiangglanda/GlandaGPU-userspace-tests

I'm sending this as an RFC because I'd like feedback on the following points before cleaning up the series further:

1) Mainline viability of custom FPGA hardware

GlandaGPU isn't a commercial chip, it's an open-source soft GPU core. The DE10-Standard itself is a regular purchasable dev board, and the RTL is public, so anyone can reproduce the exact hardware. Testing doesn't require the FPGA either, since the QEMU twin models the same interface. I know this is unusual compared to typical mainline DRM hardware. I'd like to know whether this is workable, or whether it's a dealbreaker for mainline.

2) UAPI / ioctls / userspace

The driver currently exposes three fixed ioctls (CLEAR, DRAW_RECT, DRAW_LINE), mapping directly onto the current hardware command set. I have two related questions here:

a) I plan to keep developing the hardware further, which will likely mean more ioctls over time (for example, polygon/3D rendering is one direction I'm considering). Is it acceptable to keep adding a new, separate ioctl for each drawing primitive like this, or should I move to a generic command-buffer submission model instead, similar to DRM_IOCTL_VIRTGPU_EXECBUFFER in virtio_gpu, before this is treated as stable?

b) If I do end up with an ioctl-based acceleration UAPI, is writing a Mesa/Gallium3D driver the expected way to make it usable from userspace, or is there a lighter-weight option that makes more sense for a project this size?

3) x86 QEMU platform test device

To let reviewers try the driver against the QEMU twin without cross-compiling an ARM kernel and rootfs, I registered a platform device at a fixed address on x86. I'm aware this doesn't belong in the driver itself, so I isolated it into patch 3/3 and marked it "NOT FOR MERGE". Let me know if you'd rather see it dropped entirely, relying only on documentation of the QEMU-on-ARM testing path instead.

4) Pixel format conversion

glanda_pipe_update() currently does a per-pixel software conversion from XRGB8888 into the hardware's native packed format on every flip. I'm aware this is a known bottleneck. I'm planning to extend the VHDL to accept XRGB8888 natively so I can drop this conversion entirely. I'm flagging it here as a known limitation rather than blocking on it, since it's a hardware-side change and doesn't affect the UAPI.

5) drm_simple_display_pipe vs. manual plane/CRTC/encoder

Since the driver only has a single plane, CRTC, and encoder, I tried converting it to use drm_simple_display_pipe instead of the manual setup. It compiled cleanly, but my userspace tests didn't behave the way I expected. I haven't figured out why yet, so I kept the manual setup for this RFC. I'm open to revisiting this if that's the preferred direction.

Testing Status:

The driver has been tested and verified on both the QEMU fork (x86)
and physical FPGA hardware (ARM) using:
- A custom static userspace test: https://github.com/stiangglanda/GlandaGPU-userspace-tests
- modetest -M glandagpu -s 36:640x480 -v (which successfully
displays the test pattern)

Thanks for any feedback,
Leander Kieweg

Leander Kieweg (3):
dt-bindings: display: Add GlandaGPU binding drm/glanda: Add initial DRM driver for GlandaGPU NOT FOR MERGE: drm/glanda: Add x86 platform test device

 .../bindings/display/glanda,gpu.yaml          |  49 ++
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |   7 +
 drivers/gpu/drm/tiny/Kconfig                  |  25 +
 drivers/gpu/drm/tiny/Makefile                 |   1 +
 drivers/gpu/drm/tiny/glandagpu.c              | 808 ++++++++++++++++++
 include/uapi/drm/glanda_drm.h                 |  40 +
 7 files changed, 932 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
 create mode 100644 drivers/gpu/drm/tiny/glandagpu.c
 create mode 100644 include/uapi/drm/glanda_drm.h
Signature · 1 line
2.43.0
#1 lore raw

RFC PATCH 1/3 dt-bindings: display: Add GlandaGPU binding

Add Device Tree binding documentation for GlandaGPU, a custom FPGA-based 2D display controller.

---
 .../bindings/display/glanda,gpu.yaml          | 49 +++++++++++++++++++
 .../devicetree/bindings/vendor-prefixes.yaml  |  2 +
 2 files changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +49 -0
new file mode 100644
index 000000000..40304e773
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/glanda,gpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GlandaGPU 2D Hardware Accelerated Display Controller
+
+maintainers:
+  - Leander Kieweg <[email protected]>
+
+description: |
+  GlandaGPU is a custom FPGA soft-IP core providing a simple
+  2D hardware-accelerated drawing engine (clear/rect/line) with a
+  VGA-compatible display output. The register window covers a
+  combined VRAM + MMIO region, with MMIO registers at a fixed
+  offset within it.
+
+properties:
+  compatible:
+    const: glanda,gpu-1.0
+
+  reg:
+    maxItems: 1
+    description:
+      Combined VRAM + MMIO register window (VRAM at offset 0,
+      MMIO registers at offset 0x00200000 within this range).
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: Bus and pixel clock provided to the FPGA IP.
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    gpu@c0000000 {
+        compatible = "glanda,gpu-1.0";
+        reg = <0xc0000000 0x1000000>;
+        interrupts = <0 43 4>;
+        clocks = <&osc1>;
+    };
\ No newline at end of file
Documentation/devicetree/bindings/vendor-prefixes.yaml +2 -0
index b1af9deac..b8b7a5401 100644
@@ -666,6 +666,8 @@ patternProperties:
     description: Giantplus Technology Co., Ltd.
   "^gira,.*":
     description: Gira Giersiepen GmbH & Co. KG
+  "^glanda,.*":
+    description: GlandaGPU
   "^glinet,.*":
     description: GL Intelligence, Inc.
   "^globalscale,.*":
Signature · 1 line
2.43.0
#2 lore raw

Re: [RFC PATCH 1/3] dt-bindings: display: Add GlandaGPU binding

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] Missing newline at the end of the new YAML binding file. --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing newline at the end of the new YAML binding file.
--

--- Patch [1]: [RFC PATCH 1/3] dt-bindings: display: Add GlandaGPU binding --- commit 13be4be8578f4fe27e2424184082468464c6c35a
Author: Leander Kieweg <[email protected]>

dt-bindings: display: Add GlandaGPU binding

This commit adds Device Tree binding documentation for GlandaGPU, a custom FPGA-based 2D display controller.

Quoted 5 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +0 -0
new file mode 100644
index 0000000000000..40304e77382a4

[ ... ]

Documentation/devicetree/bindings/display/glanda,gpu.yaml line 49

Quoted 9 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +8 -0
+examples:
+  - |
+    gpu@c0000000 {
+        compatible = "glanda,gpu-1.0";
+        reg = <0xc0000000 0x1000000>;
+        interrupts = <0 43 4>;
+        clocks = <&osc1>;
+    };
\ No newline at end of file

[Severity: Low]
This isn't a bug, but should this new YAML file end with a newline?

Missing newlines can trigger styling warnings during yamllint checks when running make dt_binding_check.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

Re: [RFC PATCH 1/3] dt-bindings: display: Add GlandaGPU binding

In reply to Leander Kieweg

Hey,

Quoted 19 lines · Leander Kieweg

Add Device Tree binding documentation for GlandaGPU, a custom FPGA-based 2D display controller.

---
 .../bindings/display/glanda,gpu.yaml          | 49 +++++++++++++++++++
 .../devicetree/bindings/vendor-prefixes.yaml  |  2 +
 2 files changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +3 -0
new file mode 100644
index 000000000..40304e773
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---

Documentation/devicetree/bindings/display/glanda,gpu.yaml line 13

Quoted 10 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +10 -0
+$id: http://devicetree.org/schemas/display/glanda,gpu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GlandaGPU 2D Hardware Accelerated Display Controller
+
+maintainers:
+  - Leander Kieweg <[email protected]>
+
+description: |
+  GlandaGPU is a custom FPGA soft-IP core providing a simple

Sadly googling this device only shows up Phoronix and other copy-paste reporting sites. I think it'd probably be good to include a link to your project either here or in the commit message.

Quoted 11 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +11 -0
+  2D hardware-accelerated drawing engine (clear/rect/line) with a
+  VGA-compatible display output. The register window covers a
+  combined VRAM + MMIO region, with MMIO registers at a fixed
+  offset within it.
+
+properties:
+  compatible:
+    const: glanda,gpu-1.0
+
+  reg:
+    maxItems: 1

Documentation/devicetree/bindings/display/glanda,gpu.yaml line 34

Quoted 10 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +10 -0
+    description:
+      Combined VRAM + MMIO register window (VRAM at offset 0,
+      MMIO registers at offset 0x00200000 within this range).
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: Bus and pixel clock provided to the FPGA IP.

This clock sounds like it is actually required? Doubt the device works without it!
pw-bot: changes-requested

Documentation/devicetree/bindings/display/glanda,gpu.yaml line 47

Quoted 13 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +13 -0
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    gpu@c0000000 {
+        compatible = "glanda,gpu-1.0";
+        reg = <0xc0000000 0x1000000>;
+        interrupts = <0 43 4>;

Usually I would say do not include things if you don't have to, but I would much rather you did if the alternative is an interrupts property like this that doesn't resemble anything used in devicetrees.

Quoted 19 lines
Documentation/devicetree/bindings/display/glanda,gpu.yaml new file +2 -0
+        clocks = <&osc1>;
+    };
\ No newline at end of file
Documentation/devicetree/bindings/vendor-prefixes.yaml +2 -0
index b1af9deac..b8b7a5401 100644
@@ -666,6 +666,8 @@ patternProperties:
     description: Giantplus Technology Co., Ltd.
   "^gira,.*":
     description: Gira Giersiepen GmbH & Co. KG
+  "^glanda,.*":
+    description: GlandaGPU
   "^glinet,.*":
     description: GL Intelligence, Inc.
   "^globalscale,.*":
Signature · 1 line
2.43.0
#4 lore raw

Re: [RFC PATCH 1/3] dt-bindings: display: Add GlandaGPU binding

Hi Conor,

Thanks for the review.

Quoted 3 lines

Sadly googling this device only shows up Phoronix and other copy-paste reporting sites. I think it'd probably be good to include a link to your project either here or in the commit message.

Good point either way. The link was in the cover letter, but I agree it makes sense to have it directly in the description too, so it's not something you have to go dig up separately when looking at just this patch.

Quoted 3 lines

This clock sounds like it is actually required? Doubt the device works without it!
pw-bot: changes-requested

You're right, I'll move clocks into the required list.

Quoted 3 lines

Usually I would say do not include things if you don't have to, but I would much rather you did if the alternative is an interrupts property like this that doesn't resemble anything used in devicetrees.

The example was actually taken from my DE10-Standard DTS, just shortened, since I understood examples are generally expected to be minimal. For v2 I'll add interrupt-parent back in and use the GIC_SPI/IRQ_TYPE_LEVEL_HIGH macros from dt-bindings/interrupt-controller/arm-gic.h instead of raw numbers:

#include <dt-bindings/interrupt-controller/arm-gic.h>

gpu@c0000000 {
    compatible = "glanda,gpu-1.0";
    reg = <0xc0000000 0x1000000>;
    interrupt-parent = <&intc>;
    interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&osc1>;
};

Thanks again,
Leander

#5 lore raw

Re: [RFC PATCH 1/3] dt-bindings: display: Add GlandaGPU binding

Quoted 23 lines · Leander Kieweg

Hi Conor,

Thanks for the review.

Quoted 3 lines

Sadly googling this device only shows up Phoronix and other copy-paste reporting sites. I think it'd probably be good to include a link to your project either here or in the commit message.

Good point either way. The link was in the cover letter, but I agree it makes sense to have it directly in the description too, so it's not something you have to go dig up separately when looking at just this patch.

Quoted 3 lines

This clock sounds like it is actually required? Doubt the device works without it!
pw-bot: changes-requested

You're right, I'll move clocks into the required list.

Quoted 3 lines

Usually I would say do not include things if you don't have to, but I would much rather you did if the alternative is an interrupts property like this that doesn't resemble anything used in devicetrees.

Quoted 6 lines

The example was actually taken from my DE10-Standard DTS, just shortened, since I understood examples are generally expected to be minimal. For v2 I'll add interrupt-parent back in and use the GIC_SPI/IRQ_TYPE_LEVEL_HIGH macros from dt-bindings/interrupt-controller/arm-gic.h instead of raw numbers:

All of this sounds good, thanks.

Quoted 11 lines

#include <dt-bindings/interrupt-controller/arm-gic.h>

gpu@c0000000 {
    compatible = "glanda,gpu-1.0";
    reg = <0xc0000000 0x1000000>;
    interrupt-parent = <&intc>;
    interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&osc1>;
};

Thanks again,
Leander

#6 lore raw

RFC PATCH 2/3 drm/glanda: Add initial DRM driver for GlandaGPU

In reply to Leander Kieweg

Introduce the core DRM/KMS driver for GlandaGPU. This driver supports basic modesetting, atomic updates, and custom 2D hardware acceleration IOCTLs.

---
 MAINTAINERS                      |   7 +
 drivers/gpu/drm/tiny/Kconfig     |  11 +
 drivers/gpu/drm/tiny/Makefile    |   1 +
 drivers/gpu/drm/tiny/glandagpu.c | 769 +++++++++++++++++++++++++++++++
 include/uapi/drm/glanda_drm.h    |  40 ++
 5 files changed, 828 insertions(+)
 create mode 100644 drivers/gpu/drm/tiny/glandagpu.c
 create mode 100644 include/uapi/drm/glanda_drm.h
MAINTAINERS +7 -0
index 6dea93a41..49bbb5c43 100644
@@ -8076,6 +8076,13 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:	drivers/gpu/drm/gud/
 F:	include/drm/gud.h
 
+DRM DRIVER FOR GLANDAGPU
+M:	Leander Kieweg <[email protected]>
+S:	Maintained
+F:	Documentation/devicetree/bindings/display/glanda,gpu.yaml
+F:	drivers/gpu/drm/tiny/glandagpu.c
+F:	include/uapi/drm/glanda_drm.h
+
 DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
 M:	Hans de Goede <[email protected]>
 S:	Maintained
drivers/gpu/drm/tiny/Kconfig +11 -0
index f0e72d4b6..7a15bf95a 100644
@@ -56,6 +56,17 @@ config DRM_CIRRUS_QEMU
 	   - qxl (DRM_QXL, qemu -vga qxl, works best with spice)
 	   - virtio (DRM_VIRTIO_GPU), qemu -vga virtio)
 
+config DRM_GLANDA
+    tristate "GlandaGPU DRM driver"
+    depends on DRM
+    select DRM_KMS_HELPER
+    select DRM_GEM_SHMEM_HELPER
+	help
+	  DRM/KMS driver for the GlandaGPU hardware-accelerated 2D
+	  display controller (FPGA soft IP). This driver supports
+	  basic modesetting, dumb buffers, and simple 2D drawing
+	  acceleration via custom hardware IOCTLs.
+
 config DRM_GM12U320
 	tristate "GM12U320 driver for USB projectors"
 	depends on DRM && USB && MMU
drivers/gpu/drm/tiny/Makefile +1 -0
index 48d30bf61..b4fa1554a 100644
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_APPLETBDRM)		+= appletbdrm.o
 obj-$(CONFIG_DRM_ARCPGU)		+= arcpgu.o
 obj-$(CONFIG_DRM_BOCHS)			+= bochs.o
 obj-$(CONFIG_DRM_CIRRUS_QEMU)		+= cirrus-qemu.o
+obj-$(CONFIG_DRM_GLANDA) 			+= glandagpu.o
 obj-$(CONFIG_DRM_GM12U320)		+= gm12u320.o
 obj-$(CONFIG_DRM_PANEL_MIPI_DBI)	+= panel-mipi-dbi.o
 obj-$(CONFIG_DRM_PIXPAPER)              += pixpaper.o
drivers/gpu/drm/tiny/glandagpu.c new file +769 -0

769 lines not on this page · raw message

include/uapi/drm/glanda_drm.h new file +40 -0
new file mode 100644
index 000000000..35d25ba83
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef _GLANDA_DRM_H_
+#define _GLANDA_DRM_H_
+
+#include <linux/types.h>
+#include <drm/drm.h>
+
+struct glanda_clear_cmd {
+	__u32 color;
+};
+
+struct glanda_draw_rect_cmd {
+	__u16 x;
+	__u16 y;
+	__u16 w;
+	__u16 h;
+	__u32 color;
+};
+
+struct glanda_draw_line_cmd {
+	__u16 x0;
+	__u16 y0;
+	__u16 x1;
+	__u16 y1;
+	__u32 color;
+};
+
+#define DRM_GLANDA_CLEAR     0x00
+#define DRM_GLANDA_DRAW_RECT 0x01
+#define DRM_GLANDA_DRAW_LINE 0x02
+
+#define DRM_IOCTL_GLANDA_CLEAR \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_CLEAR, struct glanda_clear_cmd)
+#define DRM_IOCTL_GLANDA_DRAW_RECT \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_DRAW_RECT, struct glanda_draw_rect_cmd)
+#define DRM_IOCTL_GLANDA_DRAW_LINE \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_DRAW_LINE, struct glanda_draw_line_cmd)
+
+#endif
Signature · 1 line
2.43.0
#7 lore raw

Re: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] Plane atomic update callback calls sleeping functions, violating DRM atomic context rules.…

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Plane atomic update callback calls sleeping functions, violating DRM atomic context rules.
- [High] Manual call to drm_mode_config_cleanup causes a double-free.
- [High] Missing drm_atomic_helper_shutdown() during driver removal.
- [High] Teardown logic creates a race condition causing ioctl threads to write to unmapped MMIO memory.
- [High] IRQ handler is registered before the CRTC is initialized, creating a window for a NULL pointer dereference.
- [Medium] Direct pointer dereference of iosys_map memory bypasses abstraction and breaks on I/O memory.
- [Medium] Mishandling of platform_get_irq return value breaks probe deferral.
--

--- Patch [2]: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU --- commit ee638d3912e8ed264bf2c9ee4b6fd9350bc3726b
Author: Leander Kieweg <[email protected]>
Subject: drm/glanda: Add initial DRM driver for GlandaGPU

This commit introduces the core DRM/KMS driver for GlandaGPU. This driver supports basic modesetting, atomic updates, and custom 2D hardware acceleration IOCTLs.

Quoted 6 lines
drivers/gpu/drm/tiny/glandagpu.c new file +0 -0
new file mode 100644
index 0000000000000..8f87ae096a2e3
@@ -0,0 +1,769 @@

[ ... ]

Quoted 3 lines
drivers/gpu/drm/tiny/glandagpu.c new file +3 -0
+static void glanda_plane_atomic_update(struct drm_plane *plane,
+				       struct drm_atomic_commit *state)
+{

[ ... ]

drivers/gpu/drm/tiny/glandagpu.c line 317

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+	dma_resv_lock(shmem->base.resv, NULL);
+	ret = drm_gem_shmem_vmap_locked(shmem, &map);
+	if (ret) {
+		dma_resv_unlock(shmem->base.resv);
+		drm_err(&gdev->drm,
+			"GlandaGPU: failed to vmap GEM shmem object\n");
+		return;
+	}
+
+	mutex_lock(&gdev->lock);

[Severity: High]
DRM atomic commit callbacks execute in an atomic context and cannot sleep. Is it safe to call dma_resv_lock, drm_gem_shmem_vmap_locked, and mutex_lock here? These functions can sleep and might trigger a schedule while atomic oops or deadlocks when this path is reached via non-blocking atomic commits.

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+
+	ret = glanda_wait_idle(gdev);
+	if (ret) {
+		drm_err(&gdev->drm, "GlandaGPU: timed out waiting for idle\n");
+		mutex_unlock(&gdev->lock);
+		drm_gem_shmem_vunmap_locked(shmem, &map);
+	dma_resv_unlock(shmem->base.resv);
+		return;
+	}
+

drivers/gpu/drm/tiny/glandagpu.c line 337

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+	src_pitch = fb->pitches[0];
+	width = min_t(u32, fb->width, GLANDA_WIDTH);
+	height = min_t(u32, fb->height, GLANDA_HEIGHT);
+
+	u8 __iomem *dst_base = gdev->vram_base;
+	u8 *src_base = map.vaddr;
+	u32 y;
+
+	for (y = 0; y < height; y++) {
+		u32 *src = (u32 *)(src_base + y * src_pitch);

[Severity: Medium]
Does directly dereferencing map.vaddr break abstraction on architectures that do not support direct memory dereferencing for I/O memory? If the mapped memory is in I/O space (like an imported dma-buf), this might cause a panic. Should this use iosys_map helpers like iosys_map_rd instead?

[ ... ]

Quoted 2 lines
drivers/gpu/drm/tiny/glandagpu.c new file +2 -0
+static int glandagpu_probe(struct platform_device *pdev)
+{

[ ... ]

drivers/gpu/drm/tiny/glandagpu.c line 626

Quoted 8 lines
drivers/gpu/drm/tiny/glandagpu.c new file +8 -0
+	writel(0, gdev->mmio_base + REG_IER);
+	writel(0xFFFFFFFF, gdev->mmio_base + REG_ISR);	/* clear flags */
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret > 0) {
+		gdev->irq = ret;
+		ret = devm_request_irq(&pdev->dev, gdev->irq, glanda_irq_handler,
+				       IRQF_SHARED, "glandagpu", gdev);

[Severity: Medium]
What happens if platform_get_irq returns -EPROBE_DEFER or another negative error code? The check for ret > 0 seems to treat all negative error codes as a missing IRQ, falling back to polling permanently rather than returning the error to the driver core to retry.

[Severity: High]
Since devm_request_irq is called with IRQF_SHARED here, could the IRQ handler fire immediately before the CRTC is initialized later in probe? If the shared interrupt fires early and the INT_VSYNC bit is set, glanda_irq_handler would call drm_crtc_handle_vblank on an uninitialized crtc object, which could lead to a NULL pointer dereference.

[ ... ]

drivers/gpu/drm/tiny/glandagpu.c line 709

Quoted 4 lines
drivers/gpu/drm/tiny/glandagpu.c new file +4 -0
+err_mode_cleanup:
+	drm_mode_config_cleanup(&gdev->drm);
+	return ret;
+}

[Severity: High]
The mode config is initialized using drm_mode_config_init, which wraps drmm_mode_config_init and automatically registers a devres action for cleanup. Does calling drm_mode_config_cleanup manually here cause a double-free on probe failure?

drivers/gpu/drm/tiny/glandagpu.c line 720

Quoted 11 lines
drivers/gpu/drm/tiny/glandagpu.c new file +11 -0
+
+static void glandagpu_remove(struct platform_device *pdev)
+{
+	struct glanda_device *gdev = platform_get_drvdata(pdev);
+
+	/* Disable interrupts first so no new IRQ work can race the teardown
+	 * below, and wake up anyone still blocked in glanda_wait_idle().
+	 */
+	writel(0, gdev->mmio_base + REG_IER);
+	gdev->cmd_done = true;
+	wake_up_interruptible(&gdev->cmd_wq);

[Severity: High]
By faking a successful completion instead of signaling an abort condition, any ioctl threads sleeping in glanda_wait_idle will wake up and proceed to write to mmio_base. Without proper synchronization or a call to drm_dev_enter/exit to ensure threads finish, can these awakened threads end up writing to unmapped MMIO memory as the devres teardown proceeds?

drivers/gpu/drm/tiny/glandagpu.c line 723

Quoted 3 lines
drivers/gpu/drm/tiny/glandagpu.c new file +3 -0
+
+	drm_info(&gdev->drm, "GlandaGPU DRM Driver removed\n");
+	drm_dev_unregister(&gdev->drm);

[Severity: High]
Should this removal sequence include a call to drm_atomic_helper_shutdown to quiesce the display hardware? Without it, hardware pipelines might remain active after the module is unloaded, which could result in memory corruption or resource leaks.

drivers/gpu/drm/tiny/glandagpu.c

Quoted 2 lines
drivers/gpu/drm/tiny/glandagpu.c new file +2 -0
+	drm_mode_config_cleanup(&gdev->drm);
+}

[Severity: High]
Similar to the probe error path, does this manual call to drm_mode_config_cleanup cause a double-free on module unload, given that drm_mode_config_init already registered a devres cleanup handler?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#8 lore raw

Re: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU

In reply to Leander Kieweg

Hello,

Quoted 1 line · Leander Kieweg

+#include <linux/mod_devicetable.h> /* Device Tree parsing */

Don't include this header, I plan to get rid of it soon. of_device_id is provided by <linux/of.h>, so you can just drop this #include.

drivers/gpu/drm/tiny/glandagpu.c line 729

Quoted 3 lines
drivers/gpu/drm/tiny/glandagpu.c new file +3 -0
+/* Device Tree match table. */
+static const struct of_device_id glanda_of_match[] = {
+	{.compatible = "glanda,gpu-1.0", },

Make this:

	{ .compatible = "glanda,gpu-1.0" },

please. (That is, a space after the opening { and no comma before the closing }).

Thanks
Uwe

#9 lore raw

Re: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU

Quoted 2 lines

Don't include this header, I plan to get rid of it soon. of_device_id is provided by <linux/of.h>, so you can just drop this #include.

Will drop it, thanks.

Quoted 5 lines

Make this:

        { .compatible = "glanda,gpu-1.0" },

please. (That is, a space after the opening { and no comma before the closing }).

Will fix in v2.

Thanks for the review!
Leander

#10 lore raw

Re: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU

In reply to Leander Kieweg

Hi,

you can find a quick review below.

Am 14.07.26 um 12:11 schrieb Leander Kieweg:

Quoted 149 lines

Introduce the core DRM/KMS driver for GlandaGPU. This driver supports basic modesetting, atomic updates, and custom 2D hardware acceleration IOCTLs.

---
  MAINTAINERS                      |   7 +
  drivers/gpu/drm/tiny/Kconfig     |  11 +
  drivers/gpu/drm/tiny/Makefile    |   1 +
  drivers/gpu/drm/tiny/glandagpu.c | 769 +++++++++++++++++++++++++++++++
  include/uapi/drm/glanda_drm.h    |  40 ++
  5 files changed, 828 insertions(+)

create mode 100644 drivers/gpu/drm/tiny/glandagpu.c create mode 100644 include/uapi/drm/glanda_drm.h

MAINTAINERS +7 -0
index 6dea93a41..49bbb5c43 100644
@@ -8076,6 +8076,13 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
  F:	drivers/gpu/drm/gud/
  F:	include/drm/gud.h
 
+DRM DRIVER FOR GLANDAGPU
+M:	Leander Kieweg <[email protected]>
+S:	Maintained
+F:	Documentation/devicetree/bindings/display/glanda,gpu.yaml
+F:	drivers/gpu/drm/tiny/glandagpu.c
+F:	include/uapi/drm/glanda_drm.h
+
  DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
  M:	Hans de Goede <[email protected]>
  S:	Maintained
drivers/gpu/drm/tiny/Kconfig +11 -0
index f0e72d4b6..7a15bf95a 100644
@@ -56,6 +56,17 @@ config DRM_CIRRUS_QEMU
  	   - qxl (DRM_QXL, qemu -vga qxl, works best with spice)
  	   - virtio (DRM_VIRTIO_GPU), qemu -vga virtio)
 
+config DRM_GLANDA
+    tristate "GlandaGPU DRM driver"
+    depends on DRM
+    select DRM_KMS_HELPER
+    select DRM_GEM_SHMEM_HELPER
+	help
+	  DRM/KMS driver for the GlandaGPU hardware-accelerated 2D
+	  display controller (FPGA soft IP). This driver supports
+	  basic modesetting, dumb buffers, and simple 2D drawing
+	  acceleration via custom hardware IOCTLs.
+
  config DRM_GM12U320
  	tristate "GM12U320 driver for USB projectors"
  	depends on DRM && USB && MMU
drivers/gpu/drm/tiny/Makefile +1 -0
index 48d30bf61..b4fa1554a 100644
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_APPLETBDRM)		+= appletbdrm.o
  obj-$(CONFIG_DRM_ARCPGU)		+= arcpgu.o
  obj-$(CONFIG_DRM_BOCHS)			+= bochs.o
  obj-$(CONFIG_DRM_CIRRUS_QEMU)		+= cirrus-qemu.o
+obj-$(CONFIG_DRM_GLANDA) 			+= glandagpu.o
  obj-$(CONFIG_DRM_GM12U320)		+= gm12u320.o
  obj-$(CONFIG_DRM_PANEL_MIPI_DBI)	+= panel-mipi-dbi.o
  obj-$(CONFIG_DRM_PIXPAPER)              += pixpaper.o
drivers/gpu/drm/tiny/glandagpu.c new file +76 -0
new file mode 100644
index 000000000..8f87ae096
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>	/* udelay (polling) */
+#include <linux/mod_devicetable.h>	/* Device Tree parsing */
+#include <linux/of.h>
+#include <linux/slab.h>		/* GFP_KERNEL */
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/iosys-map.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_vblank.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_print.h>
+
+#include <uapi/drm/glanda_drm.h>
+
+/* Hardware Constants */
+#define GLANDA_WIDTH      640
+#define GLANDA_HEIGHT     480
+#define GLANDA_VRAM_SIZE  (GLANDA_WIDTH * GLANDA_HEIGHT * 4)
+#define GLANDA_MMIO_SIZE  32
+#define GLANDA_MMIO_OFFSET 0x00200000
+
+/* Base addresses used by the x86 test device. */
+#define BRIDGE_BASE       0xC0000000
+#define GLANDA_VRAM_BASE  (BRIDGE_BASE + 0x00000000)
+#define GLANDA_MMIO_BASE  (BRIDGE_BASE + GLANDA_MMIO_OFFSET)
+#define GLANDA_BASE_SIZE  (BRIDGE_BASE + 0x01000000 - 1)
+
+/* Register Offsets */
+#define REG_STATUS  0x00
+#define REG_CTRL    0x04
+#define REG_COORD0  0x08
+#define REG_COORD1  0x0C
+#define REG_COLOR   0x10
+#define REG_ISR     0x14
+#define REG_IER     0x18
+
+/* Bit Masks */
+#define INT_DONE    BIT(0)
+#define INT_VSYNC   BIT(1)
+
+#define STATUS_BUSY BIT(0)
+#define CMD_CLEAR   (0x1)
+#define CMD_RECT    (0x2)

drivers/gpu/drm/tiny/glandagpu.c line 86

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+#define CMD_LINE    (0x3)
+#define CTRL_START  BIT(4)
+
+struct glanda_device {
+	struct drm_device drm;
+
+	/* hw */
+	void __iomem *mmio_base;
+	void __iomem *vram_base;
+	struct device *dev;

Please use drm_device.dev instead.

Quoted 212 lines
drivers/gpu/drm/tiny/glandagpu.c new file +212 -0
+	phys_addr_t vram_phys;
+
+	int irq;
+	wait_queue_head_t cmd_wq;
+	bool cmd_done;
+
+	struct mutex lock; /* for every ineration with the hardware */
+
+	/* drm */
+	struct drm_plane primary_plane;
+	struct drm_crtc crtc;
+	struct drm_encoder encoder;
+	struct drm_connector connector;
+};
+
+#define to_glanda(dev) container_of(dev, struct glanda_device, drm)
+
+static const u32 glanda_plane_formats[] = {
+	DRM_FORMAT_XRGB8888,
+};
+
+static int glanda_wait_idle(struct glanda_device *gdev)
+{
+	int ret;
+	unsigned int status;
+
+	status = readl(gdev->mmio_base + REG_STATUS);
+	if (!(status & STATUS_BUSY))
+		return 0;
+
+	if (gdev->irq < 0) {
+		int timeout = 10000;
+
+		do {
+			status = readl(gdev->mmio_base + REG_STATUS);
+			if (!(status & STATUS_BUSY))
+				return 0;
+			udelay(1);
+		} while (--timeout > 0);
+
+		dev_err(gdev->dev, "GlandaGPU: polling wait_idle timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	gdev->cmd_done = false;
+
+	ret = wait_event_interruptible_timeout(gdev->cmd_wq, gdev->cmd_done ||
+					       !(readl(gdev->mmio_base + REG_STATUS) & STATUS_BUSY),
+					       msecs_to_jiffies(500));
+
+	if (ret == 0) {
+		dev_err(gdev->dev, "GlandaGPU: IRQ wait_idle timeout\n");
+		return -ETIMEDOUT;
+	} else if (ret < 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static int glanda_hw_clear(struct glanda_device *gdev, int color)
+{
+	u32 ctrl;
+	int ret;
+
+	if (mutex_lock_interruptible(&gdev->lock))
+		return -ERESTARTSYS;
+
+	ret = glanda_wait_idle(gdev);
+	if (ret) {
+		mutex_unlock(&gdev->lock);
+		return ret;
+	}
+
+	writel(color, gdev->mmio_base + REG_COLOR);
+	ctrl = CTRL_START | CMD_CLEAR;
+	writel(ctrl, gdev->mmio_base + REG_CTRL);
+
+	mutex_unlock(&gdev->lock);
+	return 0;
+}
+
+static int glanda_hw_draw_rect(struct glanda_device *gdev,
+			       int x, int y, int w, int h, int color)
+{
+	u32 coord0, coord1, ctrl;
+	int ret;
+
+	if (mutex_lock_interruptible(&gdev->lock))
+		return -ERESTARTSYS;
+
+	ret = glanda_wait_idle(gdev);
+	if (ret) {
+		mutex_unlock(&gdev->lock);
+		return ret;
+	}
+
+	coord0 = (y << 16) | (x & 0x3FF);
+	coord1 = (h << 16) | (w & 0x3FF);
+
+	writel(coord0, gdev->mmio_base + REG_COORD0);
+	writel(coord1, gdev->mmio_base + REG_COORD1);
+	writel(color, gdev->mmio_base + REG_COLOR);
+
+	ctrl = CTRL_START | CMD_RECT;
+	writel(ctrl, gdev->mmio_base + REG_CTRL);
+
+	mutex_unlock(&gdev->lock);
+	return 0;
+}
+
+static int glanda_hw_draw_line(struct glanda_device *gdev,
+			       int x1, int y1, int x2, int y2, int color)
+{
+	u32 coord0, coord1, ctrl;
+	int ret;
+
+	if (mutex_lock_interruptible(&gdev->lock))
+		return -ERESTARTSYS;
+
+	ret = glanda_wait_idle(gdev);
+	if (ret) {
+		mutex_unlock(&gdev->lock);
+		return ret;
+	}
+
+	coord0 = (y1 << 16) | (x1 & 0x3FF);
+	coord1 = (y2 << 16) | (x2 & 0x3FF);
+
+	writel(coord0, gdev->mmio_base + REG_COORD0);
+	writel(coord1, gdev->mmio_base + REG_COORD1);
+	writel(color, gdev->mmio_base + REG_COLOR);
+
+	ctrl = CTRL_START | CMD_LINE;
+	writel(ctrl, gdev->mmio_base + REG_CTRL);
+
+	mutex_unlock(&gdev->lock);
+	return 0;
+}
+
+static int glanda_drm_ioctl_clear(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv)
+{
+	struct glanda_device *gdev = to_glanda(dev);
+	struct glanda_clear_cmd *cmd = data;
+
+	return glanda_hw_clear(gdev, cmd->color);
+}
+
+static bool glanda_rect_cmd_is_valid(const struct glanda_draw_rect_cmd *cmd)
+{
+	if (cmd->x >= GLANDA_WIDTH || cmd->y >= GLANDA_HEIGHT)
+		return false;
+	if (cmd->w > GLANDA_WIDTH || cmd->h > GLANDA_HEIGHT)
+		return false;
+	if (cmd->x + cmd->w > GLANDA_WIDTH)
+		return false;
+	if (cmd->y + cmd->h > GLANDA_HEIGHT)
+		return false;
+
+	return true;
+}
+
+static int glanda_drm_ioctl_draw_rect(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv)
+{
+	struct glanda_device *gdev = to_glanda(dev);
+	struct glanda_draw_rect_cmd *cmd = data;
+
+	if (!glanda_rect_cmd_is_valid(cmd))
+		return -EINVAL;
+
+	return glanda_hw_draw_rect(gdev, cmd->x, cmd->y, cmd->w, cmd->h,
+				   cmd->color);
+}
+
+static bool glanda_line_cmd_is_valid(const struct glanda_draw_line_cmd *cmd)
+{
+	if (cmd->x0 >= GLANDA_WIDTH || cmd->y0 >= GLANDA_HEIGHT)
+		return false;
+	if (cmd->x1 >= GLANDA_WIDTH || cmd->y1 >= GLANDA_HEIGHT)
+		return false;
+
+	return true;
+}
+
+static int glanda_drm_ioctl_draw_line(struct drm_device *dev, void *data,
+				      struct drm_file *file_priv)
+{
+	struct glanda_device *gdev = to_glanda(dev);
+	struct glanda_draw_line_cmd *cmd = data;
+
+	if (!glanda_line_cmd_is_valid(cmd))
+		return -EINVAL;
+
+	return glanda_hw_draw_line(gdev, cmd->x0, cmd->y0, cmd->x1, cmd->y1,
+				   cmd->color);
+}
+
+static void glanda_plane_atomic_update(struct drm_plane *plane,
+				       struct drm_atomic_commit *state)
+{
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+	struct drm_framebuffer *fb = new_state->fb;
+	struct glanda_device *gdev = to_glanda(plane->dev);
+	struct drm_gem_shmem_object *shmem;
+	struct iosys_map map;
+	u32 src_pitch;
+	u32 width;
+	u32 height;
+	int ret;
+

drivers/gpu/drm/tiny/glandagpu.c line 308

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+	if (!fb)
+		return;
+
+	shmem = to_drm_gem_shmem_obj(fb->obj[0]);
+	if (!shmem)  {
+		drm_err(&gdev->drm, "GlandaGPU: framebuffer is not a shmem GEM object\n");
+		return;
+	}
+
+	dma_resv_lock(shmem->base.resv, NULL);

Synchronization happens with drm_gem_fb_begin_cpu_access(). No need to take the lock here.

drivers/gpu/drm/tiny/glandagpu.c line 309

Quoted 1 line
drivers/gpu/drm/tiny/glandagpu.c new file +1 -0
+	ret = drm_gem_shmem_vmap_locked(shmem, &map);

This vmap should not happen here, but in begin_fb_access.  We have helpers that set up the mapping for your driver. Take a look at DRM_GEM_SHADOW_PLANE_HELPER_FUNCS and DRM_GEM_SHADOW_PLANE_FUNCS and the drivers that use them.

Quoted 28 lines
drivers/gpu/drm/tiny/glandagpu.c new file +28 -0
+	if (ret) {
+		dma_resv_unlock(shmem->base.resv);
+		drm_err(&gdev->drm,
+			"GlandaGPU: failed to vmap GEM shmem object\n");
+		return;
+	}
+
+	mutex_lock(&gdev->lock);
+
+	ret = glanda_wait_idle(gdev);
+	if (ret) {
+		drm_err(&gdev->drm, "GlandaGPU: timed out waiting for idle\n");
+		mutex_unlock(&gdev->lock);
+		drm_gem_shmem_vunmap_locked(shmem, &map);
+	dma_resv_unlock(shmem->base.resv);
+		return;
+	}
+
+	src_pitch = fb->pitches[0];
+	width = min_t(u32, fb->width, GLANDA_WIDTH);
+	height = min_t(u32, fb->height, GLANDA_HEIGHT);
+
+	u8 __iomem *dst_base = gdev->vram_base;
+	u8 *src_base = map.vaddr;
+	u32 y;
+
+	for (y = 0; y < height; y++) {
+		u32 *src = (u32 *)(src_base + y * src_pitch);

drivers/gpu/drm/tiny/glandagpu.c line 347

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+		u32 __iomem *dst = (u32 __iomem *)(dst_base + y * GLANDA_WIDTH * sizeof(u32));
+		u32 x;
+
+		for (x = 0; x < width; x++) {
+			u32 pixel = src[x];
+			u32 packed = ((pixel >> 12) & 0x0F00) |
+				((pixel >> 8) & 0x00F0) |
+				((pixel >> 4) & 0x000F);
+
+			writel_relaxed(packed, &dst[x]);

That's confusing. Each pixel is 32 bit. But each color channel is only 4 bit. So there's a 20-bit hole in each pixel, right?   Why don't you use 16 bit per pixel?

Quoted 105 lines
drivers/gpu/drm/tiny/glandagpu.c new file +105 -0
+		}
+	}
+
+	mutex_unlock(&gdev->lock);
+	drm_gem_shmem_vunmap_locked(shmem, &map);
+	dma_resv_unlock(shmem->base.resv);
+}
+
+static int glanda_plane_atomic_check(struct drm_plane *plane,
+				     struct drm_atomic_commit *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+	struct drm_crtc_state *crtc_state;
+
+	if (!new_plane_state->crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
+
+	return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
+		DRM_PLANE_NO_SCALING, DRM_PLANE_NO_SCALING,
+		false,	/* can_position */
+		false	/* can_update_disabled */);
+}
+
+static const struct drm_plane_helper_funcs glanda_plane_helper_funcs = {
+	.atomic_update = glanda_plane_atomic_update,
+	.atomic_check = glanda_plane_atomic_check,
+};
+
+static const struct drm_plane_funcs glanda_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int glanda_connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode) {
+		dev_err(connector->dev->dev, "GlandaGPU: failed to create display mode\n");
+		return 0;
+	}
+
+	/* Standard VGA timing: 640x480 @ 60 Hz. */
+	mode->hdisplay = 640;
+	mode->hsync_start = 656;
+	mode->hsync_end = 752;
+	mode->htotal = 800;
+
+	mode->vdisplay = 480;
+	mode->vsync_start = 490;
+	mode->vsync_end = 492;
+	mode->vtotal = 525;
+
+	mode->clock = 25175;	/* 25.175 MHz pixel clock */
+
+	mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static enum drm_connector_status glanda_connector_detect(struct drm_connector
+							 *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static int glanda_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct glanda_device *gdev = to_glanda(crtc->dev);
+	u32 ier;
+
+	ier = readl(gdev->mmio_base + REG_IER);
+	writel(ier | INT_VSYNC, gdev->mmio_base + REG_IER);
+
+	return 0;
+}
+
+static void glanda_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct glanda_device *gdev = to_glanda(crtc->dev);
+	u32 ier = readl(gdev->mmio_base + REG_IER);
+
+	writel(ier & ~INT_VSYNC, gdev->mmio_base + REG_IER);
+}
+
+static void glanda_crtc_atomic_enable(struct drm_crtc *crtc,
+				      struct drm_atomic_commit *state)
+{
+	drm_crtc_vblank_on(crtc);
+}
+
+static void glanda_crtc_atomic_disable(struct drm_crtc *crtc,
+				       struct drm_atomic_commit *state)
+{

drivers/gpu/drm/tiny/glandagpu.c line 462

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+	drm_crtc_vblank_off(crtc);
+}
+
+static void glanda_crtc_atomic_flush(struct drm_crtc *crtc,
+				     struct drm_atomic_commit *state)
+{
+	struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc);
+	struct drm_pending_vblank_event *event;
+
+	if (new_state && new_state->event) {

I don't think it is possible that new_state is NULL.

Quoted 45 lines
drivers/gpu/drm/tiny/glandagpu.c new file +45 -0
+		event = new_state->event;
+
+		new_state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+
+		if (drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static const struct drm_crtc_funcs glanda_crtc_funcs = {
+	.destroy = drm_crtc_cleanup,
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank = glanda_crtc_enable_vblank,
+	.disable_vblank = glanda_crtc_disable_vblank,
+};
+
+static const struct drm_crtc_helper_funcs glanda_crtc_helper_funcs = {
+	.atomic_enable = glanda_crtc_atomic_enable,
+	.atomic_disable = glanda_crtc_atomic_disable,
+	.atomic_flush = glanda_crtc_atomic_flush,
+};
+
+static const struct drm_connector_helper_funcs glanda_connector_helper_funcs = {
+	.get_modes = glanda_connector_get_modes,
+};
+
+static const struct drm_connector_funcs glanda_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.detect = glanda_connector_detect,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+

drivers/gpu/drm/tiny/glandagpu.c line 517

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+static const struct drm_framebuffer_funcs glanda_fb_funcs = {
+	.destroy = drm_gem_fb_destroy,
+	.create_handle = drm_gem_fb_create_handle,
+	.dirty = drm_atomic_helper_dirtyfb,
+};
+
+static struct drm_framebuffer *glanda_fb_create(struct drm_device *dev,
+						struct drm_file *file,
+						const struct drm_format_info *info,
+						const struct drm_mode_fb_cmd2 *mode_cmd)

No need for this function. drm_gem_fb_create_with_dirty() is exactly what you're looking for.

Quoted 16 lines
drivers/gpu/drm/tiny/glandagpu.c new file +16 -0
+{
+	return drm_gem_fb_create_with_funcs(dev, file, info, mode_cmd, &glanda_fb_funcs);
+}
+
+static const struct drm_mode_config_funcs glanda_mode_config_funcs = {
+	.fb_create = glanda_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+/*
+ * RFC NOTE: These three fixed-function ioctls (clear/rect/line) are a
+ * minimal placeholder UAPI to demonstrate the hardware's 2D drawing
+ * capability end-to-end. Given plans to add polygon/3D rendering support
+ * in the future, feedback is explicitly requested on whether a generic
+ * command-buffer submission ioctl (similar to virtio_gpu) would

drivers/gpu/drm/tiny/glandagpu.c line 543

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+ * be a better long-term UAPI direction before this is treated as stable.
+ */
+static const struct drm_ioctl_desc glanda_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(GLANDA_CLEAR, glanda_drm_ioctl_clear,
+			  DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(GLANDA_DRAW_RECT, glanda_drm_ioctl_draw_rect,
+			  DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(GLANDA_DRAW_LINE, glanda_drm_ioctl_draw_line,
+			  DRM_AUTH | DRM_RENDER_ALLOW),
+};

Drop these ioctls entirely. As I outlined in my reply to the cover letter, these will not work with DRM as they are now. Especially since you're using gem-shmem as a shadow buffer. All your rendering happens in system memory in user space in software.

Quoted 122 lines
drivers/gpu/drm/tiny/glandagpu.c new file +122 -0
+
+DEFINE_DRM_GEM_FOPS(glanda_drm_fops);
+
+static const struct drm_driver glanda_drm_driver = {
+	.driver_features =
+	    DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_RENDER,
+	.name = "glandagpu",
+	.desc = "GlandaGPU Hardware Accelerated DRM Driver",
+	.major = 1,
+	.minor = 0,
+	.fops = &glanda_drm_fops,
+	.dumb_create = drm_gem_shmem_dumb_create,
+	.ioctls = glanda_ioctls,
+	.num_ioctls = ARRAY_SIZE(glanda_ioctls),
+};
+
+static irqreturn_t glanda_irq_handler(int irq, void *dev_id)
+{
+	struct glanda_device *gdev = dev_id;
+
+	if (!gdev || !gdev->mmio_base)
+		return IRQ_NONE;
+
+	u32 isr = readl(gdev->mmio_base + REG_ISR);
+
+	if (!isr)
+		return IRQ_NONE;
+
+	if (isr & INT_DONE) {
+		gdev->cmd_done = true;
+		wake_up_interruptible(&gdev->cmd_wq);
+	}
+
+	if (isr & INT_VSYNC)
+		drm_crtc_handle_vblank(&gdev->crtc);
+
+	/* Clear interrupt(W1C) */
+	writel(isr, gdev->mmio_base + REG_ISR);
+	return IRQ_HANDLED;
+}
+
+static int glandagpu_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct glanda_device *gdev;
+	int ret;
+
+	dev_info(&pdev->dev, "GlandaGPU Probe started\n");
+
+	gdev = devm_drm_dev_alloc(&pdev->dev, &glanda_drm_driver, struct glanda_device, drm);
+	if (IS_ERR(gdev))
+		return PTR_ERR(gdev);
+
+	gdev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, gdev);
+
+	mutex_init(&gdev->lock);
+	/* Interrupt setup */
+	init_waitqueue_head(&gdev->cmd_wq);
+	gdev->irq = -1;
+	/* Map VRAM */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	gdev->vram_phys = res->start;
+	gdev->vram_base = devm_ioremap(&pdev->dev, res->start, GLANDA_VRAM_SIZE);
+	gdev->mmio_base = devm_ioremap(&pdev->dev, res->start + GLANDA_MMIO_OFFSET,
+				       GLANDA_MMIO_SIZE);
+
+	if (!gdev->vram_base || !gdev->mmio_base) {
+		drm_err(&gdev->drm, "failed to ioremap\n");
+		return -ENOMEM;
+	}
+
+	writel(0, gdev->mmio_base + REG_IER);
+	writel(0xFFFFFFFF, gdev->mmio_base + REG_ISR);	/* clear flags */
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret > 0) {
+		gdev->irq = ret;
+		ret = devm_request_irq(&pdev->dev, gdev->irq, glanda_irq_handler,
+				       IRQF_SHARED, "glandagpu", gdev);
+		if (ret) {
+			drm_err(&gdev->drm, "Failed to request IRQ %d\n",
+				gdev->irq);
+			return ret;
+		}
+
+		writel(INT_DONE, gdev->mmio_base + REG_IER);
+		drm_info(&gdev->drm, "IRQ %d requested and enabled\n", gdev->irq);
+	} else {
+		drm_warn(&gdev->drm, "No IRQ found, falling back to polling\n");
+	}
+
+	/* DRM mode config */
+	drm_mode_config_init(&gdev->drm);
+	gdev->drm.mode_config.min_width = 640;
+	gdev->drm.mode_config.min_height = 480;
+	gdev->drm.mode_config.max_width = 640;
+	gdev->drm.mode_config.max_height = 480;
+	gdev->drm.mode_config.funcs = &glanda_mode_config_funcs;
+
+	ret = drm_universal_plane_init(&gdev->drm, &gdev->primary_plane, 1 << 0,
+				       &glanda_plane_funcs,
+				       glanda_plane_formats,
+				       ARRAY_SIZE(glanda_plane_formats), NULL,
+				       DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret) {
+		drm_err(&gdev->drm, "Failed to initialize primary plane\n");
+		goto err_mode_cleanup;
+	}
+	drm_plane_helper_add(&gdev->primary_plane, &glanda_plane_helper_funcs);
+
+	/* VBlank init */
+	ret = drm_vblank_init(&gdev->drm, 1);
+	if (ret) {
+		drm_err(&gdev->drm, "Failed to initialize vblank\n");
+		goto err_mode_cleanup;
+	}
+
+	/* CRTC init */

drivers/gpu/drm/tiny/glandagpu.c line 675

Quoted 10 lines
drivers/gpu/drm/tiny/glandagpu.c new file +10 -0
+	ret = drm_crtc_init_with_planes(&gdev->drm, &gdev->crtc,
+					&gdev->primary_plane, NULL,
+					&glanda_crtc_funcs, NULL);
+	if (ret) {
+		drm_err(&gdev->drm, "Failed to initialize CRTC with planes\n");
+		goto err_mode_cleanup;
+	}
+	drm_crtc_helper_add(&gdev->crtc, &glanda_crtc_helper_funcs);
+
+	ret = drm_simple_encoder_init(&gdev->drm, &gdev->encoder, DRM_MODE_ENCODER_DAC);

This function is going away. You can open-code it instead.

Best regards
Thomas

Quoted 140 lines
drivers/gpu/drm/tiny/glandagpu.c new file +94 -0
+	if (ret) {
+		drm_err(&gdev->drm, "Failed to initialize encoder\n");
+		goto err_mode_cleanup;
+	}
+	gdev->encoder.possible_crtcs = 1;
+
+	ret = drm_connector_init(&gdev->drm, &gdev->connector,
+				 &glanda_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+	if (ret) {
+		drm_err(&gdev->drm, "Failed to initialize connector\n");
+		goto err_mode_cleanup;
+	}
+	drm_connector_helper_add(&gdev->connector, &glanda_connector_helper_funcs);
+
+	drm_connector_attach_encoder(&gdev->connector, &gdev->encoder);
+
+	/* Populate connector state early so userspace can enumerate modes. */
+	mutex_lock(&gdev->drm.mode_config.mutex);
+	drm_helper_probe_single_connector_modes(&gdev->connector, 1024, 768);
+	mutex_unlock(&gdev->drm.mode_config.mutex);
+
+	drm_mode_config_reset(&gdev->drm);
+
+	ret = drm_dev_register(&gdev->drm, 0);
+	if (ret)
+		goto err_mode_cleanup;
+
+	drm_info(&gdev->drm, "GlandaGPU DRM Initialized (/dev/dri/cardX created)\n");
+	return 0;
+
+err_mode_cleanup:
+	drm_mode_config_cleanup(&gdev->drm);
+	return ret;
+}
+
+static void glandagpu_remove(struct platform_device *pdev)
+{
+	struct glanda_device *gdev = platform_get_drvdata(pdev);
+
+	/* Disable interrupts first so no new IRQ work can race the teardown
+	 * below, and wake up anyone still blocked in glanda_wait_idle().
+	 */
+	writel(0, gdev->mmio_base + REG_IER);
+	gdev->cmd_done = true;
+	wake_up_interruptible(&gdev->cmd_wq);
+
+	drm_info(&gdev->drm, "GlandaGPU DRM Driver removed\n");
+	drm_dev_unregister(&gdev->drm);
+	drm_mode_config_cleanup(&gdev->drm);
+}
+
+/* Device Tree match table. */
+static const struct of_device_id glanda_of_match[] = {
+	{.compatible = "glanda,gpu-1.0", },
+	{ /* end of table */  }
+};
+
+MODULE_DEVICE_TABLE(of, glanda_of_match);
+
+static struct platform_driver glandagpu_driver = {
+	.driver = {
+		.name = "glandagpu",
+	   .of_match_table = glanda_of_match,
+	},
+	.probe = glandagpu_probe,
+	.remove = glandagpu_remove,
+};
+
+static int __init glandagpu_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&glandagpu_driver);
+	if (ret) {
+		pr_err("GlandaGPU: Failed to register platform driver\n");
+		return ret;
+	}
+
+	pr_info("GlandaGPU: Module loaded successfully\n");
+	return 0;
+}
+
+static void __exit glandagpu_exit(void)
+{
+	platform_driver_unregister(&glandagpu_driver);
+	pr_info("GlandaGPU: Module unloaded\n");
+}
+
+module_init(glandagpu_init);
+module_exit(glandagpu_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Leander Kieweg <[email protected]>");
+MODULE_DESCRIPTION("DRM driver for GlandaGPU, an FPGA-based 2D GPU with VGA output");
include/uapi/drm/glanda_drm.h new file +40 -0
new file mode 100644
index 000000000..35d25ba83
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef _GLANDA_DRM_H_
+#define _GLANDA_DRM_H_
+
+#include <linux/types.h>
+#include <drm/drm.h>
+
+struct glanda_clear_cmd {
+	__u32 color;
+};
+
+struct glanda_draw_rect_cmd {
+	__u16 x;
+	__u16 y;
+	__u16 w;
+	__u16 h;
+	__u32 color;
+};
+
+struct glanda_draw_line_cmd {
+	__u16 x0;
+	__u16 y0;
+	__u16 x1;
+	__u16 y1;
+	__u32 color;
+};
+
+#define DRM_GLANDA_CLEAR     0x00
+#define DRM_GLANDA_DRAW_RECT 0x01
+#define DRM_GLANDA_DRAW_LINE 0x02
+
+#define DRM_IOCTL_GLANDA_CLEAR \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_CLEAR, struct glanda_clear_cmd)
+#define DRM_IOCTL_GLANDA_DRAW_RECT \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_DRAW_RECT, struct glanda_draw_rect_cmd)
+#define DRM_IOCTL_GLANDA_DRAW_LINE \
+	DRM_IOW(DRM_COMMAND_BASE + DRM_GLANDA_DRAW_LINE, struct glanda_draw_line_cmd)
+
+#endif
Signature · 6 lines
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, (HRB 36809, AG Nürnberg)
#11 lore raw

Re: [RFC PATCH 2/3] drm/glanda: Add initial DRM driver for GlandaGPU

Hi Thomas,

Thanks for the detailed review, going through each point:

Quoted 1 line

Please use drm_device.dev instead.

Will drop this field and use gdev->drm.dev everywhere instead.

Quoted 9 lines

Synchronization happens with drm_gem_fb_begin_cpu_access(). No need to take the lock here.

Quoted 1 line

+ ret = drm_gem_shmem_vmap_locked(shmem, &map);

This vmap should not happen here, but in begin_fb_access. We have helpers that set up the mapping for your driver. Take a look at DRM_GEM_SHADOW_PLANE_HELPER_FUNCS and DRM_GEM_SHADOW_PLANE_FUNCS and the drivers that use them.

I wasn't aware these helpers existed, this looks like exactly the right fit for what I'm doing (software render into shmem, blit into hardware VRAM on flip). I'll switch to DRM_GEM_SHADOW_PLANE_HELPER_FUNCS/DRM_GEM_SHADOW_PLANE_FUNCS and drop the manual dma_resv_lock/vmap_locked handling entirely for v2.

Quoted 3 lines

That's confusing. Each pixel is 32 bit. But each color channel is only 4
bit. So there's a 20-bit hole in each pixel, right? Why don't you use
16 bit per pixel?

You're right that it's wasteful as-is. One thing worth mentioning though, my VRAM write port on the hardware side is a fixed 32-bit Avalon-MM slave without byteenable support. The hardware itself always reads the lower 12 bits and ignores the rest, so that part is fine regardless of write width, but I'm not certain the interconnect would reliably place a 16-bit write into the lower half of the 32-bit word rather than, say, the upper half depending on address alignment, without byteenable support to make that explicit.

I'm already planning to update the hardware to accept XRGB8888 natively, which will need the full 32-bit width regardless and removes the CPU-side pixel conversion entirely, so I'll leave this as a 32-bit write for now and address it as part of that hardware change rather than adding byteenable support for what would only be a temporary fix.

Quoted 1 line

I don't think it is possible that new_state is NULL.

I'll drop the redundant check.

Quoted 2 lines

No need for this function. drm_gem_fb_create_with_dirty() is exactly what you're looking for.

Thanks, I'll drop glanda_fb_create()/glanda_fb_funcs and just assign drm_gem_fb_create_with_dirty directly.

Quoted 4 lines

Drop these ioctls entirely. As I outlined in my reply to the cover letter, these will not work with DRM as they are now. Especially since you're using gem-shmem as a shadow buffer. All your rendering happens in system memory in user space in software.

Agreed, as mentioned in my reply to the cover letter, these are gone in v2.

Quoted 1 line

This function is going away. You can open-code it instead.

Will do, thanks for the heads up.

I'll include all of this in v2 along with what we discussed on the cover letter thread. Thanks again for the thorough review.

Best regards,
Leander

#12 lore raw

RFC PATCH 3/3 NOT FOR MERGE: drm/glanda: Add x86 platform test device

In reply to Leander Kieweg

Register a mock platform device on x86 architectures to allow testing the GlandaGPU driver inside QEMU without cross-compiling.

---
 drivers/gpu/drm/tiny/Kconfig     | 14 ++++++++++++
 drivers/gpu/drm/tiny/glandagpu.c | 39 ++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)
drivers/gpu/drm/tiny/Kconfig +14 -0
index 7a15bf95a..156713746 100644
@@ -67,6 +67,20 @@ config DRM_GLANDA
 	  basic modesetting, dumb buffers, and simple 2D drawing
 	  acceleration via custom hardware IOCTLs.
 
+config DRM_GLANDA_X86_TEST
+    bool "Register a fixed-address test device on x86 (QEMU only)"
+    depends on DRM_GLANDA && X86
+    default n
+    help
+      Registers a platform device at a hardcoded physical address
+      (0xC0000000) for testing GlandaGPU against a QEMU-based digital
+      twin without needing a devicetree.
+
+      WARNING: this blindly ioremaps a fixed physical address range.
+      Do NOT enable this on real x86 hardware. This exists solely to
+      let reviewers test the driver in QEMU without cross-compiling
+      an ARM kernel/rootfs. Leave disabled otherwise.
+
 config DRM_GM12U320
 	tristate "GM12U320 driver for USB projectors"
 	depends on DRM && USB && MMU
drivers/gpu/drm/tiny/glandagpu.c +39 -0
index 8f87ae096..c1e9370f2 100644
@@ -741,6 +741,34 @@ static struct platform_driver glandagpu_driver = {
 	.remove = glandagpu_remove,
 };
 
+#ifdef CONFIG_DRM_GLANDA_X86_TEST
+static struct platform_device *pdev_x86;
+
+static struct resource glandagpu_resources[] = {
+	[0] = {			/* Single resource covering VRAM and MMIO. */
+	       .start = BRIDGE_BASE,
+	       .end = GLANDA_BASE_SIZE,
+	       .flags = IORESOURCE_MEM,},
+	[1] = {			/* IRQ */
+	       .start = 11,
+	       .end = 11,
+	       .flags = IORESOURCE_IRQ,},
+};
+
+static int glandagpu_register_x86_test_device(void)
+{
+	pdev_x86 = platform_device_register_simple("glandagpu", -1,
+						   glandagpu_resources,
+						   ARRAY_SIZE(glandagpu_resources));
+	if (IS_ERR(pdev_x86)) {
+		pr_err("GlandaGPU: Failed to register platform device\n");
+		return PTR_ERR(pdev_x86);
+	}
+
+	return 0;
+}
+#endif
+
 static int __init glandagpu_init(void)
 {
 	int ret;
@@ -750,6 +778,13 @@ static int __init glandagpu_init(void)
 		pr_err("GlandaGPU: Failed to register platform driver\n");
 		return ret;
 	}
+#ifdef CONFIG_DRM_GLANDA_X86_TEST
+	ret = glandagpu_register_x86_test_device();
+	if (ret) {
+		platform_driver_unregister(&glandagpu_driver);
+		return ret;
+	}
+#endif
 
 	pr_info("GlandaGPU: Module loaded successfully\n");
 	return 0;
@@ -757,6 +792,10 @@ static int __init glandagpu_init(void)
 
 static void __exit glandagpu_exit(void)
 {
+#ifdef CONFIG_DRM_GLANDA_X86_TEST
+	if (pdev_x86)
+		platform_device_unregister(pdev_x86);
+#endif
 	platform_driver_unregister(&glandagpu_driver);
 	pr_info("GlandaGPU: Module unloaded\n");
 }
Signature · 1 line
2.43.0
#13 lore raw

Re: [RFC PATCH 3/3] NOT FOR MERGE: drm/glanda: Add x86 platform test device

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing DRM unplug protection in IOCTL handlers allows Use-After-Unmap of MMIO resources.
- [High] The plane atomic update callback calls multiple sleeping functions while executing in atomic context.
- [High] Premature shared IRQ registration causes a NULL pointer dereference if the interrupt fires before CRTC initialization. --

--- Patch [3]: [RFC PATCH 3/3] NOT FOR MERGE: drm/glanda: Add x86 platform test device --- commit 6108d4cd0cea176f9e750373bc9ba705d3d2a6c7
Author: Leander Kieweg <[email protected]>

NOT FOR MERGE: drm/glanda: Add x86 platform test device

Register a mock platform device on x86 architectures to allow testing the GlandaGPU driver inside QEMU without cross-compiling.

drivers/gpu/drm/tiny/glandagpu.c

Quoted 4 lines
drivers/gpu/drm/tiny/glandagpu.c +0 -0
index 8f87ae096a2e3..c1e9370f2a4b1 100644

[Severity: High]
This is a pre-existing issue, but is it possible for a concurrent ioctl to access unmapped MMIO memory during device removal?

Looking at glandagpu_remove(), it wakes up blocked ioctls:

drivers/gpu/drm/tiny/glandagpu.c:glandagpu_remove() {
    ...
	gdev->cmd_done = true;
	wake_up_interruptible(&gdev->cmd_wq);
    ...
}

However, it lacks drm_dev_unplug() and drm_dev_enter()/drm_dev_exit() synchronization. When glandagpu_remove() returns, the devres framework automatically unmaps gdev->mmio_base. The forcibly awakened ioctl could then resume and write directly to the unmapped MMIO memory:

drivers/gpu/drm/tiny/glandagpu.c:glanda_hw_clear() {
    ...
	writel(color, gdev->mmio_base + REG_COLOR);
    ...
}

Could this cause a kernel panic due to a use-after-unmap page fault?

[Severity: High]
This isn't a bug introduced by this patch, but does the plane atomic update callback sleep while in atomic context?

Userspace can initiate a non-blocking DRM atomic commit, which executes glanda_plane_atomic_update() in an atomic context. However, it appears to call multiple sleeping functions:

drivers/gpu/drm/tiny/glandagpu.c:glanda_plane_atomic_update() {
    ...
	dma_resv_lock(shmem->base.resv, NULL);
	ret = drm_gem_shmem_vmap_locked(shmem, &map);
    ...
	mutex_lock(&gdev->lock);

	ret = glanda_wait_idle(gdev);
    ...
}

Since glanda_wait_idle() internally uses wait_event_interruptible_timeout(), could these calls cause system instability, scheduling-while-atomic warnings, or deadlocks in the DRM commit worker?

[Severity: High]
This is a pre-existing issue, but could premature shared IRQ registration cause a NULL pointer dereference during probe?

In glandagpu_probe(), devm_request_irq() is called with IRQF_SHARED before the DRM CRTC is initialized by drm_crtc_init_with_planes():

drivers/gpu/drm/tiny/glandagpu.c:glandagpu_probe() {
    ...
		ret = devm_request_irq(&pdev->dev, gdev->irq, glanda_irq_handler,
				       IRQF_SHARED, "glandagpu", gdev);
    ...
	ret = drm_crtc_init_with_planes(&gdev->drm, &gdev->crtc,
    ...
}

If another device on the same shared line triggers an interrupt immediately, the handler reads the raw REG_ISR without masking it against the interrupt enable register (REG_IER). If the INT_VSYNC bit is set, it proceeds to handle the vblank:

drivers/gpu/drm/tiny/glandagpu.c:glanda_irq_handler() {
    ...
	if (isr & INT_VSYNC)
		drm_crtc_handle_vblank(&gdev->crtc);
    ...
}

Since gdev->crtc is still zero-initialized at this point, could this dereference a NULL crtc->dev pointer and lead to a kernel panic?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#14 lore raw
In reply to Leander Kieweg

Hi

Am 14.07.26 um 12:11 schrieb Leander Kieweg:

Quoted 8 lines

Hi everyone,

This series adds a DRM driver for GlandaGPU, a custom open-source 2D graphics core I designed in VHDL. It currently runs on a Cyclone V SoC (Terasic DE10-Standard), with VGA output at 640x480@60Hz. I also built a QEMU fork that emulates the same MMIO/VRAM interface as a digital twin, so I can develop and test without needing the FPGA board.

That is a pretty cool project.

Quoted 10 lines
   Hardware/VHDL:    https://github.com/stiangglanda/GlandaGPU
   QEMU fork:        https://github.com/stiangglanda/qemu-glandagpu
   Userspace tests:  https://github.com/stiangglanda/GlandaGPU-userspace-tests

I'm sending this as an RFC because I'd like feedback on the following points before cleaning up the series further:

1) Mainline viability of custom FPGA hardware

Quoted 8 lines

GlandaGPU isn't a commercial chip, it's an open-source soft GPU core. The DE10-Standard itself is a regular purchasable dev board, and the RTL is public, so anyone can reproduce the exact hardware. Testing doesn't require the FPGA either, since the QEMU twin models the same interface. I know this is unusual compared to typical mainline DRM hardware. I'd like to know whether this is workable, or whether it's a dealbreaker for mainline.

We already have a driver for a RasPi-based USB display that someone made in their spare time. So being a hobbyist project is not a problem per se.

Quoted 6 lines

2) UAPI / ioctls / userspace

The driver currently exposes three fixed ioctls (CLEAR, DRAW_RECT, DRAW_LINE), mapping directly onto the current hardware command set. I have two related questions here:

2d primitives are likely not very useful for DRM. The canonical reference of why is at [1]. The tl;dr is that there's no standard API, and GPU-CPU transfers and setup costs are too high to make it significantly faster than software rendering.

Various people have proposed to add some form of 2d pipeline to DRM, but nothing concrete has ever emerged.

Conceptually, DRM doesn't really render anything. It composes the screen from already-rendered buffers. Rendering to these buffers is mostly done by Mesa drivers with some help from DRM's kernel drivers. If you want hardware rendering, you'd need memory management for off-screen rendering that Mesa can use independently from display output. Therefore these simple draw and clear primitives aren't that useful. You need to design a full rendering pipeline instead.

[1] https://blog.ffwll.ch/2018/08/no-2d-in-drm.html

Quoted 9 lines

a) I plan to keep developing the hardware further, which will likely mean more ioctls over time (for example, polygon/3D rendering is one direction I'm considering). Is it acceptable to keep adding a new, separate ioctl for each drawing primitive like this, or should I move to a generic command-buffer submission model instead, similar to DRM_IOCTL_VIRTGPU_EXECBUFFER in virtio_gpu, before this is treated as stable?

Using a single ioctl per command will kill performance. So, if anything, you'd want the command-buffer model.  To make it fast for 2d primitives, you'd likely have to model it like a 3d pipeline: have all 2d graphics buffers in the display memory already and submit a large batch of rendering commands that generate the entire screen at once.

Quoted 5 lines

b) If I do end up with an ioctl-based acceleration UAPI, is writing a Mesa/Gallium3D driver the expected way to make it usable from userspace, or is there a lighter-weight option that makes more sense for a project this size?

Rule of thumb is that you need a working user-space side for ioctls. Mesa would be the premier target for 3d.

I'm not much involved in Mesa, but I think Mesa is quickly moving towards programmable pipelines. Getting drivers for fixed-function hardware merged might be hard.

How complicated is it to model a stream processor (i.e. GPU core) in VHDL?

Quoted 10 lines

3) x86 QEMU platform test device

To let reviewers try the driver against the QEMU twin without cross-compiling an ARM kernel and rootfs, I registered a platform device at a fixed address on x86. I'm aware this doesn't belong in the driver itself, so I isolated it into patch 3/3 and marked it "NOT FOR MERGE". Let me know if you'd rather see it dropped entirely, relying only on documentation of the QEMU-on-ARM testing path instead.

Can you use a PCI device for that and let the kernel do all the work? See [2] for how to get a PCI device id.

[2] https://www.qemu.org/docs/master/specs/pci-ids.html

Quoted 10 lines

4) Pixel format conversion

glanda_pipe_update() currently does a per-pixel software conversion from XRGB8888 into the hardware's native packed format on every flip. I'm aware this is a known bottleneck. I'm planning to extend the VHDL to accept XRGB8888 natively so I can drop this conversion entirely. I'm flagging it here as a known limitation rather than blocking on it, since it's a hardware-side change and doesn't affect the UAPI.

That's indeed a good thing to have in hardware.

I mentioned that the 2d/3d rendering is probably complicated to get done. If I may suggest an alternative, you could implement additional features of the mode-setting pipeline. Besides the primary plane that your hardware already supports, you could add a cursor plane. Or you could add overlay planes for displaying YUV formats (i.e., video frames). Or you could implement existing DRM properties, such as scaling, background colors, or HDR.  These features are already supported by user space. Your device would be usable immediately.

Quoted 9 lines

5) drm_simple_display_pipe vs. manual plane/CRTC/encoder

Since the driver only has a single plane, CRTC, and encoder, I tried converting it to use drm_simple_display_pipe instead of the manual setup. It compiled cleanly, but my userspace tests didn't behave the way I expected. I haven't figured out why yet, so I kept the manual setup for this RFC. I'm open to revisiting this if that's the preferred direction.

Glad to hear it didn't work. drm_simple_display_pipe is obsolete and on its way out. Please don't use it.

Best regards
Thomas

Quoted 29 lines

Testing Status:

The driver has been tested and verified on both the QEMU fork (x86)
and physical FPGA hardware (ARM) using:
- A custom static userspace test: https://github.com/stiangglanda/GlandaGPU-userspace-tests
- modetest -M glandagpu -s 36:640x480 -v (which successfully
displays the test pattern)

Thanks for any feedback,
Leander Kieweg

Leander Kieweg (3):
dt-bindings: display: Add GlandaGPU binding drm/glanda: Add initial DRM driver for GlandaGPU NOT FOR MERGE: drm/glanda: Add x86 platform test device

  .../bindings/display/glanda,gpu.yaml          |  49 ++
  .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
  MAINTAINERS                                   |   7 +
  drivers/gpu/drm/tiny/Kconfig                  |  25 +
  drivers/gpu/drm/tiny/Makefile                 |   1 +
  drivers/gpu/drm/tiny/glandagpu.c              | 808 ++++++++++++++++++
  include/uapi/drm/glanda_drm.h                 |  40 +
  7 files changed, 932 insertions(+)

create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml create mode 100644 drivers/gpu/drm/tiny/glandagpu.c
create mode 100644 include/uapi/drm/glanda_drm.h

Signature · 6 lines
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, (HRB 36809, AG Nürnberg)
#15 lore raw

Hi Thomas,

Thanks a lot for the detailed feedback, this is exactly the kind of guidance I was hoping for.

Quoted 2 lines

We already have a driver for a RasPi-based USB display that someone made in their spare time. So being a hobbyist project is not a problem per se.

Good to know, thanks for clarifying that.

Quoted 16 lines

2d primitives are likely not very useful for DRM. The canonical reference of why is at [1]. The tl;dr is that there's no standard API, and GPU-CPU transfers and setup costs are too high to make it significantly faster than software rendering.

Various people have proposed to add some form of 2d pipeline to DRM, but nothing concrete has ever emerged.

Conceptually, DRM doesn't really render anything. It composes the screen from already-rendered buffers. Rendering to these buffers is mostly done by Mesa drivers with some help from DRM's kernel drivers. If you want hardware rendering, you'd need memory management for off-screen rendering that Mesa can use independently from display output. Therefore these simple draw and clear primitives aren't that useful. You need to design a full rendering pipeline instead.

Quoted 1 line

[1] https://blog.ffwll.ch/2018/08/no-2d-in-drm.html

Sima's blog post that you linked makes the reasoning much clearer than what I'd pieced together on my own. I understand this means the current fixed-function
ioctls are really only a placeholder, not something to build on further as is.

Quoted 5 lines

Using a single ioctl per command will kill performance. So, if anything, you'd want the command-buffer model. To make it fast for 2d primitives, you'd likely have to model it like a 3d pipeline: have all 2d graphics buffers in the display memory already and submit a large batch of rendering commands that generate the entire screen at once.

That's a helpful concrete direction, thank you. I'll look into what a command-buffer submission model would need to look like for this.

Quoted 8 lines

Rule of thumb is that you need a working user-space side for ioctls. Mesa would be the premier target for 3d.

I'm not much involved in Mesa, but I think Mesa is quickly moving towards programmable pipelines. Getting drivers for fixed-function hardware merged might be hard.

How complicated is it to model a stream processor (i.e. GPU core) in VHDL?

I think it's doable at a small scope. Some memory to hold uploaded bytecode, a minimal CPU core (I'd probably base the ISA on a reduced RISC-V subset, mainly to get an existing compiler toolchain for free) that processes it and writes the result into the framebuffer. That part sounds genuinely fun to build, and I'm actually looking forward to it, though it's a fair amount of work and I'll need to find the time for it.

For throughput I'd design the core with multiple instances in mind from the start, e.g. 4 cores each responsible for a quarter of the screen, and scale that further once the basic version works. I'd still get a single instance working first before parallelizing it.

Quoted 4 lines

Can you use a PCI device for that and let the kernel do all the work? See [2] for how to get a PCI device id.

[2] https://www.qemu.org/docs/master/specs/pci-ids.html

I want to make sure I understand this correctly before I start on it. My real hardware (DE10-Standard) stays exactly as it is, still using platform_driver via devicetree, completely unchanged. Only the QEMU device would change, from the current fixed-address platform device to a proper virtual PCI device (with a reserved vendor/device ID and BARs for VRAM/MMIO). On the driver side, that would mean adding pci_driver support alongside the existing platform_driver, so the exact same driver would run on both, just with two different probe functions depending on whether it's found via devicetree (real hardware) or via PCI enumeration (QEMU). Is that right?

Quoted 10 lines

That's indeed a good thing to have in hardware.

I mentioned that the 2d/3d rendering is probably complicated to get done. If I may suggest an alternative, you could implement additional features of the mode-setting pipeline. Besides the primary plane that your hardware already supports, you could add a cursor plane. Or you could add overlay planes for displaying YUV formats (i.e., video frames). Or you could implement existing DRM properties, such as scaling, background colors, or HDR. These features are already supported by user space. Your device would be usable immediately.

This is a great suggestion, thank you. A hardware cursor plane actually fits something I'd already been considering for the hardware side, so that'll be my next bigger hardware update.

Scaling looks approachable too, and I like that it wouldn't need extra memory on the hardware side, so I'm adding that to the list as well. Background color I think I can already cover with the existing CLEAR command.

YUV overlay support is more future work for me, my current memory layout would need a bigger restructuring first to fit that in given how limited memory is on this hardware, but the idea itself sounds appealing since the VHDL side looks fairly contained. HDR isn't realistic here either way, my test display can't do it and I'd expect to hit memory limits before anything else.

Quoted 2 lines

Glad to hear it didn't work. drm_simple_display_pipe is obsolete and on its way out. Please don't use it.

Good to know, I'll drop that idea entirely then.

Given all this, my plan for v2 is to drop the ioctl-based UAPI entirely (removing the custom ioctls means there's no UAPI to keep stable, which takes the pressure off getting the command-buffer design right immediately), address the style/review comments from Uwe and from your code review on patch 2, switch the QEMU test path from the fixed-address platform device to a proper PCI device as discussed above, and possibly add background color support since that doesn't require any hardware changes. The cursor plane, scaling, and the stream processor work will be separate follow-ups once the hardware side catches up.

Thanks again for taking the time, this gives me a lot to work with.

Best regards,
Leander

#16 lore raw

Hi

Am 16.07.26 um 23:19 schrieb Leander Kieweg:

Quoted 64 lines

Hi Thomas,

Thanks a lot for the detailed feedback, this is exactly the kind of guidance I was hoping for.

Quoted 2 lines

We already have a driver for a RasPi-based USB display that someone made in their spare time. So being a hobbyist project is not a problem per se.

Good to know, thanks for clarifying that.

Quoted 16 lines

2d primitives are likely not very useful for DRM. The canonical reference of why is at [1]. The tl;dr is that there's no standard API, and GPU-CPU transfers and setup costs are too high to make it significantly faster than software rendering.

Various people have proposed to add some form of 2d pipeline to DRM, but nothing concrete has ever emerged.

Conceptually, DRM doesn't really render anything. It composes the screen from already-rendered buffers. Rendering to these buffers is mostly done by Mesa drivers with some help from DRM's kernel drivers. If you want hardware rendering, you'd need memory management for off-screen rendering that Mesa can use independently from display output. Therefore these simple draw and clear primitives aren't that useful. You need to design a full rendering pipeline instead.

Quoted 1 line

[1] https://blog.ffwll.ch/2018/08/no-2d-in-drm.html

Sima's blog post that you linked makes the reasoning much clearer than what I'd pieced together on my own. I understand this means the current fixed-function
ioctls are really only a placeholder, not something to build on further as is.

Quoted 5 lines

Using a single ioctl per command will kill performance. So, if anything, you'd want the command-buffer model. To make it fast for 2d primitives, you'd likely have to model it like a 3d pipeline: have all 2d graphics buffers in the display memory already and submit a large batch of rendering commands that generate the entire screen at once.

That's a helpful concrete direction, thank you. I'll look into what a command-buffer submission model would need to look like for this.

Quoted 8 lines

Rule of thumb is that you need a working user-space side for ioctls. Mesa would be the premier target for 3d.

I'm not much involved in Mesa, but I think Mesa is quickly moving towards programmable pipelines. Getting drivers for fixed-function hardware merged might be hard.

How complicated is it to model a stream processor (i.e. GPU core) in VHDL?

I think it's doable at a small scope. Some memory to hold uploaded bytecode, a minimal CPU core (I'd probably base the ISA on a reduced RISC-V subset, mainly to get an existing compiler toolchain for free) that processes it and writes the result into the framebuffer. That part sounds genuinely fun to build, and I'm actually looking forward to it, though it's a fair amount of work and I'll need to find the time for it.

For throughput I'd design the core with multiple instances in mind from the start, e.g. 4 cores each responsible for a quarter of the screen, and scale that further once the basic version works. I'd still get a single instance working first before parallelizing it.

Quoted 4 lines

Can you use a PCI device for that and let the kernel do all the work? See [2] for how to get a PCI device id.

[2] https://www.qemu.org/docs/master/specs/pci-ids.html

Quoted 10 lines

I want to make sure I understand this correctly before I start on it. My real hardware (DE10-Standard) stays exactly as it is, still using platform_driver via devicetree, completely unchanged. Only the QEMU device would change, from the current fixed-address platform device to a proper virtual PCI device (with a reserved vendor/device ID and BARs for VRAM/MMIO). On the driver side, that would mean adding pci_driver support alongside the existing platform_driver, so the exact same driver would run on both, just with two different probe functions depending on whether it's found via devicetree (real hardware) or via PCI enumeration (QEMU). Is that right?

Yes, exactly this. I'm not sure if you can have both in a single module .ko file, but your driver can have multiple modules.

Quoted 11 lines
Quoted 11 lines

That's indeed a good thing to have in hardware.

I mentioned that the 2d/3d rendering is probably complicated to get done. If I may suggest an alternative, you could implement additional features of the mode-setting pipeline. Besides the primary plane that your hardware already supports, you could add a cursor plane. Or you could add overlay planes for displaying YUV formats (i.e., video frames). Or you could implement existing DRM properties, such as scaling, background colors, or HDR. These features are already supported by user space. Your device would be usable immediately.

Quoted 8 lines

This is a great suggestion, thank you. A hardware cursor plane actually fits something I'd already been considering for the hardware side, so that'll be my next bigger hardware update.

Scaling looks approachable too, and I like that it wouldn't need extra memory on the hardware side, so I'm adding that to the list as well. Background color I think I can already cover with the existing CLEAR command.

To clarify: background color is a hardware feature.  Pixels have an alpha channel that blends them with the underlying plane.  On the primary plane, there's no underlying plane. So it would be black. Some hardware also allows to configure this to some other color per CRTC.

Quoted 7 lines

YUV overlay support is more future work for me, my current memory layout would need a bigger restructuring first to fit that in given how limited memory is on this hardware, but the idea itself sounds appealing since the VHDL side looks fairly contained. HDR isn't realistic here either way, my test display can't do it and I'd expect to hit memory limits before anything else.

Indeed. I think, if you want to add some more advanced features, you will need more memory for the device.

Best regards
Thomas

Quoted 20 lines
Quoted 3 lines

Glad to hear it didn't work. drm_simple_display_pipe is obsolete and on its way out. Please don't use it.

Good to know, I'll drop that idea entirely then.

Given all this, my plan for v2 is to drop the ioctl-based UAPI entirely (removing the custom ioctls means there's no UAPI to keep stable, which takes the pressure off getting the command-buffer design right immediately), address the style/review comments from Uwe and from your code review on patch 2, switch the QEMU test path from the fixed-address platform device to a proper PCI device as discussed above, and possibly add background color support since that doesn't require any hardware changes. The cursor plane, scaling, and the stream processor work will be separate follow-ups once the hardware side catches up.

Thanks again for taking the time, this gives me a lot to work with.

Best regards,
Leander

Signature · 6 lines
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, (HRB 36809, AG Nürnberg)