* [PATCH v2 0/2] drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU)
@ 2026-07-30 17:36 Leander Kieweg
2026-07-30 17:36 ` [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding Leander Kieweg
2026-07-30 17:36 ` [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU Leander Kieweg
0 siblings, 2 replies; 10+ messages in thread
From: Leander Kieweg @ 2026-07-30 17:36 UTC (permalink / raw)
To: dri-devel, devicetree
Cc: airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh,
krzk+dt, conor+dt, u.kleine-koenig, Leander Kieweg
This is v2 of the GlandaGPU DRM driver series [1], addressing review
feedback from Thomas Zimmermann, Conor Dooley, Uwe Kleine-König, and
the automated review bot on v1.
GlandaGPU is a small VHDL soft-IP 2D display controller, currently
targeting a Terasic DE10-Standard (Cyclone V SoC). This series has
been tested both against a QEMU digital twin and on real hardware.
Hardware/VHDL: https://github.com/stiangglanda/GlandaGPU
QEMU fork: https://github.com/stiangglanda/qemu-glandagpu
Userspace tests: https://github.com/stiangglanda/GlandaGPU-userspace-tests
Changes since v1:
dt-bindings:
- add a link to the GlandaGPU project in the binding description
- add clocks to the required properties
- fix the example to use interrupt-parent and the GIC_SPI/
IRQ_TYPE_LEVEL_HIGH macros instead of raw numbers
- fix missing trailing newline
driver core:
- drop the struct device *dev field, use drm_device.dev instead
- open-code drm_encoder_init() instead of drm_simple_encoder_init(),
which is being phased out
- switch the plane update path from manual dma_resv_lock()/
drm_gem_shmem_vmap_locked() to DRM_GEM_SHADOW_PLANE_HELPER_FUNCS/
DRM_GEM_SHADOW_PLANE_FUNCS. This also fixes sleeping calls being
made from atomic-commit context and a raw map.vaddr dereference,
both flagged by automated review
- use drm_gem_fb_create_with_dirty() instead of a custom fb_create
implementation
- remove the redundant new_state == NULL check in the CRTC flush
helper
- remove the ioctl-based UAPI (CLEAR/RECT/LINE) entirely. As Thomas
pointed out in v1, fixed-function 2D ioctls aren't a good fit for
mainline DRM, and userspace rendering into the shadow-plane
framebuffer covers the same functionality
- fix a double drm_mode_config_cleanup() call in the probe and
remove error paths (devres already handles this)
- call drm_atomic_helper_shutdown() on remove
- fix a teardown race where in-flight work could touch already
unmapped MMIO, using drm_dev_unplug()/drm_dev_enter()/
drm_dev_exit()
- move IRQ registration to after CRTC initialization, avoiding a
potential NULL pointer dereference from an early-firing shared IRQ
- fix platform_get_irq() error handling. Negative error codes such
as -EPROBE_DEFER were previously misread as "no IRQ available"
new in v2:
- add PCI probe support alongside the existing platform_driver/
devicetree path, backed by a matching QEMU device model, so the
driver can be tested against a virtual device without needing
devicetree or real hardware. Real hardware continues to use the
platform_driver path unchanged. This replaces the old x86 test
device patch 3/3, reducing the series from 3 to 2 patches.
Known open item: the QEMU test device currently uses a placeholder
ID from the range QEMU reserves for experimental, unregistered use
(1af4:10f0, see [2]). I've requested a permanent
ID from Gerd Hoffmann and will post a follow-up once it's assigned.
[1] https://lore.kernel.org/dri-devel/20260714101146.200416-1-kieweg.leander@gmail.com/T/#t
[2] https://www.qemu.org/docs/master/specs/pci-ids.html
Leander Kieweg (2):
dt-bindings: display: Add GlandaGPU binding
drm/glanda: Add initial DRM driver for GlandaGPU
.../bindings/display/glanda,gpu.yaml | 55 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 6 +
drivers/gpu/drm/tiny/Kconfig | 11 +
drivers/gpu/drm/tiny/Makefile | 1 +
drivers/gpu/drm/tiny/glandagpu.c | 579 ++++++++++++++++++
6 files changed, 654 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
create mode 100644 drivers/gpu/drm/tiny/glandagpu.c
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding
2026-07-30 17:36 [PATCH v2 0/2] drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU) Leander Kieweg
@ 2026-07-30 17:36 ` Leander Kieweg
2026-07-31 9:53 ` Krzysztof Kozlowski
2026-07-30 17:36 ` [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU Leander Kieweg
1 sibling, 1 reply; 10+ messages in thread
From: Leander Kieweg @ 2026-07-30 17:36 UTC (permalink / raw)
To: dri-devel, devicetree
Cc: airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh,
krzk+dt, conor+dt, u.kleine-koenig, Leander Kieweg
Add Device Tree binding documentation for GlandaGPU, a custom
FPGA-based 2D display controller.
For hardware designs and RTL sources, see:
https://github.com/stiangglanda/GlandaGPU
Signed-off-by: Leander Kieweg <kieweg.leander@gmail.com>
---
.../bindings/display/glanda,gpu.yaml | 55 +++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
2 files changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
diff --git a/Documentation/devicetree/bindings/display/glanda,gpu.yaml b/Documentation/devicetree/bindings/display/glanda,gpu.yaml
new file mode 100644
index 000000000..988a30946
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/glanda,gpu.yaml
@@ -0,0 +1,55 @@
+# 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 <kieweg.leander@gmail.com>
+
+description: |
+ GlandaGPU is a custom FPGA soft-IP core providing a simple
+ 2D hardware-accelerated drawing engine with a VGA-compatible
+ display output. The register window covers a combined VRAM + MMIO
+ region, with MMIO registers at a fixed offset within it.
+
+ For hardware designs and RTL sources, see:
+ https://github.com/stiangglanda/GlandaGPU
+
+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
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #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>;
+ };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index b1af9deac..b8b7a5401 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -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,.*":
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU
2026-07-30 17:36 [PATCH v2 0/2] drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU) Leander Kieweg
2026-07-30 17:36 ` [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding Leander Kieweg
@ 2026-07-30 17:36 ` Leander Kieweg
2026-07-30 17:52 ` sashiko-bot
2026-07-31 9:56 ` Krzysztof Kozlowski
1 sibling, 2 replies; 10+ messages in thread
From: Leander Kieweg @ 2026-07-30 17:36 UTC (permalink / raw)
To: dri-devel, devicetree
Cc: airlied, simona, maarten.lankhorst, mripard, tzimmermann, robh,
krzk+dt, conor+dt, u.kleine-koenig, Leander Kieweg
Introduce the core DRM/KMS driver for GlandaGPU. This driver
supports basic modesetting, atomic updates via shadow plane helpers,
and optional QEMU PCI probing alongside the platform driver.
Signed-off-by: Leander Kieweg <kieweg.leander@gmail.com>
---
MAINTAINERS | 6 +
drivers/gpu/drm/tiny/Kconfig | 11 +
drivers/gpu/drm/tiny/Makefile | 1 +
drivers/gpu/drm/tiny/glandagpu.c | 579 +++++++++++++++++++++++++++++++
4 files changed, 597 insertions(+)
create mode 100644 drivers/gpu/drm/tiny/glandagpu.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 6dea93a41..8bb0513eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8076,6 +8076,12 @@ 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 <kieweg.leander@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/display/glanda,gpu.yaml
+F: drivers/gpu/drm/tiny/glandagpu.c
+
DRM DRIVER FOR GRAIN MEDIA GM12U320 PROJECTORS
M: Hans de Goede <hansg@kernel.org>
S: Maintained
diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
index f0e72d4b6..f5aaf499e 100644
--- a/drivers/gpu/drm/tiny/Kconfig
+++ b/drivers/gpu/drm/tiny/Kconfig
@@ -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 display controller
+ (FPGA soft IP). This driver supports basic modesetting,
+ dumb buffers, and atomic updates via shadow planes.
+ It also provides PCI probing for QEMU testing.
+
config DRM_GM12U320
tristate "GM12U320 driver for USB projectors"
depends on DRM && USB && MMU
diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile
index 48d30bf61..b4fa1554a 100644
--- a/drivers/gpu/drm/tiny/Makefile
+++ b/drivers/gpu/drm/tiny/Makefile
@@ -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
diff --git a/drivers/gpu/drm/tiny/glandagpu.c b/drivers/gpu/drm/tiny/glandagpu.c
new file mode 100644
index 000000000..e4dfddaba
--- /dev/null
+++ b/drivers/gpu/drm/tiny/glandagpu.c
@@ -0,0 +1,579 @@
+// 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/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h> /* udelay (polling) */
+#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_gem_atomic_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_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>
+
+/* 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
+
+/* QEMU test device ID, from the range reserved for experimental use (docs/specs/pci-ids.rst). */
+#define PCI_DEVICE_ID_GLANDA_GPU 0x10f0
+
+/* 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)
+#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;
+ phys_addr_t vram_phys;
+
+ int irq;
+
+ /* 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 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_shadow_plane_state *shadow_state = to_drm_shadow_plane_state(new_state);
+ struct drm_framebuffer *fb = new_state->fb;
+ struct glanda_device *gdev = to_glanda(plane->dev);
+ u32 src_pitch, width, height, x, y;
+ int idx;
+
+ if (!fb)
+ return;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
+
+ src_pitch = fb->pitches[0];
+ width = min_t(u32, fb->width, GLANDA_WIDTH);
+ height = min_t(u32, fb->height, GLANDA_HEIGHT);
+
+ for (y = 0; y < height; y++) {
+ size_t offset = y * GLANDA_WIDTH * sizeof(u32);
+ u32 __iomem *dst = (u32 __iomem *)(gdev->vram_base + offset);
+
+ for (x = 0; x < width; x++) {
+ u32 pixel = iosys_map_rd(&shadow_state->data[0],
+ y * src_pitch + x * sizeof(u32), u32);
+ u32 packed = ((pixel >> 12) & 0x0F00) |
+ ((pixel >> 8) & 0x00F0) |
+ ((pixel >> 4) & 0x000F);
+
+ writel_relaxed(packed, &dst[x]);
+ }
+ }
+
+ drm_dev_exit(idx);
+}
+
+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 = {
+ DRM_GEM_SHADOW_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,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+};
+
+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)
+{
+ 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->event) {
+ 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_encoder_funcs glanda_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+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,
+};
+
+static const struct drm_mode_config_funcs glanda_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create_with_dirty,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+DEFINE_DRM_GEM_FOPS(glanda_drm_fops);
+
+static const struct drm_driver glanda_drm_driver = {
+ .driver_features =
+ DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .name = "glandagpu",
+ .desc = "GlandaGPU Hardware Accelerated DRM Driver",
+ .major = 1,
+ .minor = 0,
+ .fops = &glanda_drm_fops,
+ .dumb_create = drm_gem_shmem_dumb_create,
+};
+
+static irqreturn_t glanda_irq_handler(int irq, void *dev_id)
+{
+ struct glanda_device *gdev = dev_id;
+ u32 isr, ier;
+
+ if (!gdev || !gdev->mmio_base)
+ return IRQ_NONE;
+
+ isr = readl(gdev->mmio_base + REG_ISR);
+ ier = readl(gdev->mmio_base + REG_IER);
+
+ if (!(isr & ier))
+ return IRQ_NONE;
+
+ if (isr & INT_VSYNC)
+ drm_crtc_handle_vblank(&gdev->crtc);
+
+ /* Clear interrupt(W1C) */
+ writel(isr, gdev->mmio_base + REG_ISR);
+ return IRQ_HANDLED;
+}
+
+/* Common DRM setup once MMIO/VRAM/IRQ are known(used by both probe paths) */
+static int glanda_drm_init(struct glanda_device *gdev, int irq)
+{
+ int ret;
+
+ gdev->irq = -1;
+
+ writel(0, gdev->mmio_base + REG_IER);
+ writel(0xFFFFFFFF, gdev->mmio_base + REG_ISR); /* clear flags */
+
+ /* 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");
+ return ret;
+ }
+ 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");
+ return ret;
+ }
+
+ /* CRTC init */
+ 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");
+ return ret;
+ }
+ drm_crtc_helper_add(&gdev->crtc, &glanda_crtc_helper_funcs);
+
+ ret = drm_encoder_init(&gdev->drm, &gdev->encoder, &glanda_encoder_funcs,
+ DRM_MODE_ENCODER_DAC, NULL);
+ if (ret) {
+ drm_err(&gdev->drm, "Failed to initialize encoder\n");
+ return ret;
+ }
+ 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");
+ return ret;
+ }
+ 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);
+
+ if (irq > 0) {
+ gdev->irq = irq;
+ ret = devm_request_irq(gdev->drm.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");
+ }
+
+ ret = drm_dev_register(&gdev->drm, 0);
+ if (ret)
+ return ret;
+
+ drm_info(&gdev->drm, "GlandaGPU DRM Initialized (/dev/dri/cardX created)\n");
+ return 0;
+}
+
+/* Shared teardown, mirrors glanda_drm_init() */
+static void glanda_drm_fini(struct glanda_device *gdev)
+{
+ drm_dev_unplug(&gdev->drm);
+ drm_atomic_helper_shutdown(&gdev->drm);
+
+ /* Disable interrupts */
+ writel(0, gdev->mmio_base + REG_IER);
+
+ drm_info(&gdev->drm, "GlandaGPU DRM Driver removed\n");
+}
+
+static int glandagpu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct glanda_device *gdev;
+ int irq;
+
+ 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);
+
+ platform_set_drvdata(pdev, gdev);
+
+ 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;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq == -ENXIO)
+ irq = -1; /* no IRQ resource, fall back to polling */
+ else if (irq < 0)
+ return irq;
+
+ return glanda_drm_init(gdev, irq);
+}
+
+static void glandagpu_remove(struct platform_device *pdev)
+{
+ glanda_drm_fini(platform_get_drvdata(pdev));
+}
+
+/* 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,
+};
+
+/* PCI probe path for the QEMU test device, real hardware uses platform_driver */
+static int glandagpu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct glanda_device *gdev;
+ int ret;
+
+ dev_info(&pdev->dev, "GlandaGPU PCI Probe started\n");
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+ pci_set_master(pdev);
+
+ ret = pcim_iomap_regions(pdev, BIT(0) | BIT(1), "glandagpu");
+ if (ret)
+ return ret;
+
+ gdev = devm_drm_dev_alloc(&pdev->dev, &glanda_drm_driver, struct glanda_device, drm);
+ if (IS_ERR(gdev))
+ return PTR_ERR(gdev);
+
+ pci_set_drvdata(pdev, gdev);
+
+ gdev->mmio_base = pcim_iomap_table(pdev)[0];
+ gdev->vram_base = pcim_iomap_table(pdev)[1];
+ gdev->vram_phys = pci_resource_start(pdev, 1);
+
+ return glanda_drm_init(gdev, pdev->irq);
+}
+
+static void glandagpu_pci_remove(struct pci_dev *pdev)
+{
+ glanda_drm_fini(pci_get_drvdata(pdev));
+}
+
+static const struct pci_device_id glanda_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_GLANDA_GPU) },
+ { /* end of table */ }
+};
+
+MODULE_DEVICE_TABLE(pci, glanda_pci_ids);
+
+static struct pci_driver glandagpu_pci_driver = {
+ .name = "glandagpu-pci",
+ .id_table = glanda_pci_ids,
+ .probe = glandagpu_pci_probe,
+ .remove = glandagpu_pci_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;
+ }
+
+ ret = pci_register_driver(&glandagpu_pci_driver);
+ if (ret) {
+ pr_err("GlandaGPU: Failed to register PCI driver\n");
+ platform_driver_unregister(&glandagpu_driver);
+ return ret;
+ }
+
+ pr_info("GlandaGPU: Module loaded successfully\n");
+ return 0;
+}
+
+static void __exit glandagpu_exit(void)
+{
+ pci_unregister_driver(&glandagpu_pci_driver);
+ 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 <kieweg.leander@gmail.com>");
+MODULE_DESCRIPTION("DRM driver for GlandaGPU, an FPGA-based 2D GPU with VGA output");
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU
2026-07-30 17:36 ` [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU Leander Kieweg
@ 2026-07-30 17:52 ` sashiko-bot
2026-07-31 9:56 ` Krzysztof Kozlowski
1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-07-30 17:52 UTC (permalink / raw)
To: Leander Kieweg; +Cc: devicetree, robh, conor+dt, dri-devel
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] Ignored return value of drm_mode_config_init().
- [High] Hardware interrupts are left enabled if drm_dev_register() fails, leading to an unhandled interrupt storm.
- [High] Missing resource size validation before I/O memory mapping allows out-of-bounds mapping and writes.
- [High] Userspace atomic commits hang when the driver falls back to polling mode because vblank events are armed but never signaled.
- [Medium] Unused INT_DONE interrupt is unconditionally enabled, risking an interrupt storm.
- [Medium] Native u32 read of little-endian pixel data breaks color conversions on big-endian CPUs.
- [Low] Missing dependencies in Kconfig for DRM_GLANDA.
- [Low] Using platform_get_irq() for an optional IRQ causes log spam.
--
--- Patch [2]: [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU ---
commit 47c9d06e7235a576cf0a48e9d5a1011c1d5c7ced
Author: Leander Kieweg <kieweg.leander@gmail.com>
Subject: drm/glanda: Add initial DRM driver for GlandaGPU
Introduce the core DRM/KMS driver for GlandaGPU. This driver supports basic
modesetting, atomic updates via shadow plane helpers, and optional QEMU PCI
probing alongside the platform driver.
> diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
> index f0e72d4b6a470..f5aaf499e34eb 100644
> --- a/drivers/gpu/drm/tiny/Kconfig
> +++ b/drivers/gpu/drm/tiny/Kconfig
[ ... ]
> @@ -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
[Severity: Low]
Does this config need to depend on MMU and PCI || COMPILE_TEST? The driver
selects DRM_GEM_SHMEM_HELPER which depends on MMU, and it calls PCI devres
functions that lack non-PCI stubs when CONFIG_PCI is disabled.
> diff --git a/drivers/gpu/drm/tiny/glandagpu.c b/drivers/gpu/drm/tiny/glandagpu.c
> new file mode 100644
> index 0000000000000..e4dfddaba4e3b
> --- /dev/null
> +++ b/drivers/gpu/drm/tiny/glandagpu.c
[ ... ]
> +static void glanda_plane_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> +{
[ ... ]
> + for (y = 0; y < height; y++) {
> + size_t offset = y * GLANDA_WIDTH * sizeof(u32);
> + u32 __iomem *dst = (u32 __iomem *)(gdev->vram_base + offset);
> +
> + for (x = 0; x < width; x++) {
> + u32 pixel = iosys_map_rd(&shadow_state->data[0],
> + y * src_pitch + x * sizeof(u32), u32);
> + u32 packed = ((pixel >> 12) & 0x0F00) |
> + ((pixel >> 8) & 0x00F0) |
> + ((pixel >> 4) & 0x000F);
> +
> + writel_relaxed(packed, &dst[x]);
> + }
> + }
[Severity: Medium]
Will this bitwise extraction read the correct color channels on big-endian
architectures? By using iosys_map_rd to read a native u32, the little-endian
DRM_FORMAT_XRGB8888 byte stream will have its bytes inverted on big-endian
CPUs.
[ ... ]
> +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->event) {
> + 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);
[Severity: High]
What happens to armed vblank events when the driver falls back to polling
mode? If there is no hardware interrupt to call drm_crtc_handle_vblank(),
these events might never be signaled, which can cause userspace page flips
to hang. Should the driver set crtc->state->no_vblank = true or bypass
drm_crtc_arm_vblank_event in polling mode?
[ ... ]
> +static int glanda_drm_init(struct glanda_device *gdev, int irq)
> +{
[ ... ]
> + writel(0, gdev->mmio_base + REG_IER);
> + writel(0xFFFFFFFF, gdev->mmio_base + REG_ISR); /* clear flags */
> +
> + /* DRM mode config */
> + drm_mode_config_init(&gdev->drm);
[Severity: High]
Is it safe to ignore the integer return value of drm_mode_config_init? If
this fails due to memory allocation issues, the DRM mode configuration state
will be partially initialized, potentially leading to NULL pointer
dereferences later when the DRM core accesses it.
[ ... ]
> + if (irq > 0) {
> + gdev->irq = irq;
> + ret = devm_request_irq(gdev->drm.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);
[Severity: Medium]
Could enabling INT_DONE cause an interrupt storm? If the hardware asserts
INT_DONE when the 2D engine is idle, and the driver never submits commands
to it, the IRQ handler might get caught in a loop continuously clearing it.
> + 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");
> + }
> +
> + ret = drm_dev_register(&gdev->drm, 0);
> + if (ret)
> + return ret;
[Severity: High]
Does this error path leave hardware interrupts enabled? If
drm_dev_register fails, the function returns without clearing REG_IER.
Since devres frees the IRQ handler, this might leave the hardware asserting
an interrupt with no kernel handler available to acknowledge it.
[ ... ]
> +static int glandagpu_probe(struct platform_device *pdev)
> +{
[ ... ]
> + 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);
[Severity: High]
Should there be a check on the underlying resource length before mapping?
Blindly mapping GLANDA_VRAM_SIZE without verifying resource_size(res) could
cause out-of-bounds mappings if the hardware resource is undersized. Later,
the shadow plane write loop in glanda_plane_atomic_update() could overrun
the boundaries and corrupt physical memory.
> + if (!gdev->vram_base || !gdev->mmio_base) {
> + drm_err(&gdev->drm, "failed to ioremap\n");
> + return -ENOMEM;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq == -ENXIO)
> + irq = -1; /* no IRQ resource, fall back to polling */
[Severity: Low]
Since the driver gracefully supports polling mode, could this use
platform_get_irq_optional instead? Using platform_get_irq logs a
false-positive error when the IRQ is not found.
[ ... ]
> +static int glandagpu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
[ ... ]
> + gdev->mmio_base = pcim_iomap_table(pdev)[0];
> + gdev->vram_base = pcim_iomap_table(pdev)[1];
> + gdev->vram_phys = pci_resource_start(pdev, 1);
[Severity: High]
Does this PCI probe path also need to validate the size of the mapped BARs
against the expected GLANDA_VRAM_SIZE and GLANDA_MMIO_SIZE to prevent
similar out-of-bounds writes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260730173643.256052-1-kieweg.leander@gmail.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding
2026-07-30 17:36 ` [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding Leander Kieweg
@ 2026-07-31 9:53 ` Krzysztof Kozlowski
2026-08-02 16:16 ` Leander Kieweg
0 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-31 9:53 UTC (permalink / raw)
To: Leander Kieweg
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
On Thu, Jul 30, 2026 at 07:36:41PM +0200, Leander Kieweg wrote:
> Add Device Tree binding documentation for GlandaGPU, a custom
> FPGA-based 2D display controller.
>
> For hardware designs and RTL sources, see:
> https://github.com/stiangglanda/GlandaGPU
>
> Signed-off-by: Leander Kieweg <kieweg.leander@gmail.com>
> ---
> .../bindings/display/glanda,gpu.yaml | 55 +++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> 2 files changed, 57 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/glanda,gpu.yaml
>
> diff --git a/Documentation/devicetree/bindings/display/glanda,gpu.yaml b/Documentation/devicetree/bindings/display/glanda,gpu.yaml
> new file mode 100644
> index 000000000..988a30946
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/glanda,gpu.yaml
> @@ -0,0 +1,55 @@
> +# 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 <kieweg.leander@gmail.com>
> +
> +description: |
> + GlandaGPU is a custom FPGA soft-IP core providing a simple
> + 2D hardware-accelerated drawing engine with a VGA-compatible
> + display output. The register window covers a combined VRAM + MMIO
> + region, with MMIO registers at a fixed offset within it.
> +
> + For hardware designs and RTL sources, see:
> + https://github.com/stiangglanda/GlandaGPU
> +
> +properties:
> + compatible:
> + const: glanda,gpu-1.0
What does this 1.0 correspond to? There is no such tag on linked Github.
> +
> + 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
> + - clocks
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #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>;
> + };
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> index b1af9deac..b8b7a5401 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
> @@ -666,6 +666,8 @@ patternProperties:
> description: Giantplus Technology Co., Ltd.
> "^gira,.*":
> description: Gira Giersiepen GmbH & Co. KG
> + "^glanda,.*":
> + description: GlandaGPU
Vendor prefixes are per actual vendor, not product.
I have doubts this is needed in the first place. As a pure FPGA design,
this cannot work alone and you cannot get it alone (at least yet,
happily to see resources proving me wrong). Thus it will be always
customized per final board design, thus that final compatible is what
matters.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU
2026-07-30 17:36 ` [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU Leander Kieweg
2026-07-30 17:52 ` sashiko-bot
@ 2026-07-31 9:56 ` Krzysztof Kozlowski
2026-08-02 16:27 ` Leander Kieweg
1 sibling, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-31 9:56 UTC (permalink / raw)
To: Leander Kieweg
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
On Thu, Jul 30, 2026 at 07:36:42PM +0200, Leander Kieweg wrote:
> + 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);
Please drop
> + } else {
> + drm_warn(&gdev->drm, "No IRQ found, falling back to polling\n");
> + }
> +
> + ret = drm_dev_register(&gdev->drm, 0);
> + if (ret)
> + return ret;
> +
> + drm_info(&gdev->drm, "GlandaGPU DRM Initialized (/dev/dri/cardX created)\n");
Please drop all such messages.
> + return 0;
> +}
> +
> +/* Shared teardown, mirrors glanda_drm_init() */
> +static void glanda_drm_fini(struct glanda_device *gdev)
> +{
> + drm_dev_unplug(&gdev->drm);
> + drm_atomic_helper_shutdown(&gdev->drm);
> +
> + /* Disable interrupts */
> + writel(0, gdev->mmio_base + REG_IER);
> +
> + drm_info(&gdev->drm, "GlandaGPU DRM Driver removed\n");
Ditto
> +}
> +
> +static int glandagpu_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct glanda_device *gdev;
> + int irq;
> +
> + dev_info(&pdev->dev, "GlandaGPU Probe started\n");
Ditto, Driver should be silent on success.
> +
> + gdev = devm_drm_dev_alloc(&pdev->dev, &glanda_drm_driver, struct glanda_device, drm);
> + if (IS_ERR(gdev))
> + return PTR_ERR(gdev);
> +
> + platform_set_drvdata(pdev, gdev);
> +
> + 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;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq == -ENXIO)
> + irq = -1; /* no IRQ resource, fall back to polling */
> + else if (irq < 0)
> + return irq;
> +
> + return glanda_drm_init(gdev, irq);
> +}
> +
> +static void glandagpu_remove(struct platform_device *pdev)
> +{
> + glanda_drm_fini(platform_get_drvdata(pdev));
> +}
> +
> +/* Device Tree match table. */
> +static const struct of_device_id glanda_of_match[] = {
> + { .compatible = "glanda,gpu-1.0" },
> + { /* end of table */ }
Drop comment, obvious.
> +};
> +
> +MODULE_DEVICE_TABLE(of, glanda_of_match);
> +
> +static struct platform_driver glandagpu_driver = {
> + .driver = {
> + .name = "glandagpu",
> + .of_match_table = glanda_of_match,
Messed indent.
> + },
> + .probe = glandagpu_probe,
> + .remove = glandagpu_remove,
> +};
> +
> +/* PCI probe path for the QEMU test device, real hardware uses platform_driver */
> +static int glandagpu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> + struct glanda_device *gdev;
> + int ret;
> +
> + dev_info(&pdev->dev, "GlandaGPU PCI Probe started\n");
Drop. Driver should be silent on success.
> +
> + ret = pcim_enable_device(pdev);
> + if (ret)
> + return ret;
> + pci_set_master(pdev);
> +
> + ret = pcim_iomap_regions(pdev, BIT(0) | BIT(1), "glandagpu");
> + if (ret)
> + return ret;
> +
> + gdev = devm_drm_dev_alloc(&pdev->dev, &glanda_drm_driver, struct glanda_device, drm);
> + if (IS_ERR(gdev))
> + return PTR_ERR(gdev);
> +
> + pci_set_drvdata(pdev, gdev);
> +
> + gdev->mmio_base = pcim_iomap_table(pdev)[0];
> + gdev->vram_base = pcim_iomap_table(pdev)[1];
> + gdev->vram_phys = pci_resource_start(pdev, 1);
> +
> + return glanda_drm_init(gdev, pdev->irq);
> +}
> +
> +static void glandagpu_pci_remove(struct pci_dev *pdev)
> +{
> + glanda_drm_fini(pci_get_drvdata(pdev));
> +}
> +
> +static const struct pci_device_id glanda_pci_ids[] = {
> + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_GLANDA_GPU) },
> + { /* end of table */ }
> +};
> +
> +MODULE_DEVICE_TABLE(pci, glanda_pci_ids);
> +
> +static struct pci_driver glandagpu_pci_driver = {
> + .name = "glandagpu-pci",
> + .id_table = glanda_pci_ids,
> + .probe = glandagpu_pci_probe,
> + .remove = glandagpu_pci_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;
> + }
> +
> + ret = pci_register_driver(&glandagpu_pci_driver);
> + if (ret) {
> + pr_err("GlandaGPU: Failed to register PCI driver\n");
> + platform_driver_unregister(&glandagpu_driver);
> + return ret;
> + }
> +
> + pr_info("GlandaGPU: Module loaded successfully\n");
Drop. Kernel does not print such messages. It pollutes dmesg even
without actual hardware.
> + return 0;
> +}
> +
> +static void __exit glandagpu_exit(void)
> +{
> + pci_unregister_driver(&glandagpu_pci_driver);
> + platform_driver_unregister(&glandagpu_driver);
> + pr_info("GlandaGPU: Module unloaded\n");
Same here.
> +}
> +
> +module_init(glandagpu_init);
> +module_exit(glandagpu_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Leander Kieweg <kieweg.leander@gmail.com>");
> +MODULE_DESCRIPTION("DRM driver for GlandaGPU, an FPGA-based 2D GPU with VGA output");
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding
2026-07-31 9:53 ` Krzysztof Kozlowski
@ 2026-08-02 16:16 ` Leander Kieweg
2026-08-03 6:32 ` Krzysztof Kozlowski
0 siblings, 1 reply; 10+ messages in thread
From: Leander Kieweg @ 2026-08-02 16:16 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
Thanks for the review.
> > +properties:
> > + compatible:
> > + const: glanda,gpu-1.0
>
> What does this 1.0 correspond to? There is no such tag on linked Github.
I've pushed a v1.0 tag to the GlandaGPU repo now, matching the
register interface described in this binding:
https://github.com/stiangglanda/GlandaGPU/releases/tag/v1.0
> > + "^glanda,.*":
> > + description: GlandaGPU
>
> Vendor prefixes are per actual vendor, not product.
>
> I have doubts this is needed in the first place. As a pure FPGA design,
> this cannot work alone and you cannot get it alone (at least yet,
> happily to see resources proving me wrong). Thus it will be always
> customized per final board design, thus that final compatible is what
> matters.
"opencores" and "openrisc" are both accepted vendor prefixes for
open-source hardware projects rather than commercial vendors, so a
project name seems reasonable to me here too.
That said, I take your point about board customization. I'm happy to
add a board-specific compatible as the primary match, with
"glanda,gpu-1.0" as the fallback, e.g.:
compatible = "terasic,de10-standard-glandagpu", "glanda,gpu-1.0";
The driver would keep matching on "glanda,gpu-1.0". Let me know
if you'd like this for v3, or if the generic compatible is enough for now,
since there's currently only one board using this core.
Best regards,
Leander
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU
2026-07-31 9:56 ` Krzysztof Kozlowski
@ 2026-08-02 16:27 ` Leander Kieweg
0 siblings, 0 replies; 10+ messages in thread
From: Leander Kieweg @ 2026-08-02 16:27 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
> Drop. Kernel does not print such messages. It pollutes dmesg even
> without actual hardware.
Thanks, all of this makes sense. I'll drop the info/debug messages on
the success paths (probe, remove, init, exit), remove the redundant
"end of table" comments, and fix the indentation on the
of_match_table line for v3.
Best regards,
Leander
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding
2026-08-02 16:16 ` Leander Kieweg
@ 2026-08-03 6:32 ` Krzysztof Kozlowski
2026-08-05 19:12 ` Leander Kieweg
0 siblings, 1 reply; 10+ messages in thread
From: Krzysztof Kozlowski @ 2026-08-03 6:32 UTC (permalink / raw)
To: Leander Kieweg
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
On 02/08/2026 18:16, Leander Kieweg wrote:
> Thanks for the review.
>
>>> +properties:
>>> + compatible:
>>> + const: glanda,gpu-1.0
>>
>> What does this 1.0 correspond to? There is no such tag on linked Github.
>
> I've pushed a v1.0 tag to the GlandaGPU repo now, matching the
> register interface described in this binding:
> https://github.com/stiangglanda/GlandaGPU/releases/tag/v1.0
>
>>> + "^glanda,.*":
>>> + description: GlandaGPU
>>
>> Vendor prefixes are per actual vendor, not product.
>>
>> I have doubts this is needed in the first place. As a pure FPGA design,
>> this cannot work alone and you cannot get it alone (at least yet,
>> happily to see resources proving me wrong). Thus it will be always
>> customized per final board design, thus that final compatible is what
>> matters.
>
> "opencores" and "openrisc" are both accepted vendor prefixes for
> open-source hardware projects rather than commercial vendors, so a
> project name seems reasonable to me here too.
>
> That said, I take your point about board customization. I'm happy to
> add a board-specific compatible as the primary match, with
> "glanda,gpu-1.0" as the fallback, e.g.:
>
> compatible = "terasic,de10-standard-glandagpu", "glanda,gpu-1.0";
>
> The driver would keep matching on "glanda,gpu-1.0". Let me know
> if you'd like this for v3, or if the generic compatible is enough for now,
> since there's currently only one board using this core.
openrisc is arch.
opencores is entire org. Are you saying that "Glanda" is this one:
https://www.glanda.com.au/
and opencores releases entire SoCs, thus they integrate their own IPs
there into final product. I agree though, that opencores should not
define standalone generic compatibles.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding
2026-08-03 6:32 ` Krzysztof Kozlowski
@ 2026-08-05 19:12 ` Leander Kieweg
0 siblings, 0 replies; 10+ messages in thread
From: Leander Kieweg @ 2026-08-05 19:12 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: dri-devel, devicetree, airlied, simona, maarten.lankhorst,
mripard, tzimmermann, robh, krzk+dt, conor+dt, u.kleine-koenig
> openrisc is arch.
> opencores is entire org. Are you saying that "Glanda" is this one:
> https://www.glanda.com.au/
> and opencores releases entire SoCs, thus they integrate their own IPs
> there into final product. I agree though, that opencores should not
> define standalone generic compatibles.
Fair, those precedents don't really hold up then.
No relation to glanda.com.au, I wasn't even aware of that company.
This is just a personal hobby project, not a company or organization.
Given that, what would you suggest instead? I'm open to using a
board-specific compatible as the primary match as discussed, or
whatever naming scheme you'd consider appropriate for an individual's
hobby project.
Best regards,
Leander
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-05 19:12 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 17:36 [PATCH v2 0/2] drm: Add DRM driver for GlandaGPU (VHDL soft-IP GPU) Leander Kieweg
2026-07-30 17:36 ` [PATCH v2 1/2] dt-bindings: display: Add GlandaGPU binding Leander Kieweg
2026-07-31 9:53 ` Krzysztof Kozlowski
2026-08-02 16:16 ` Leander Kieweg
2026-08-03 6:32 ` Krzysztof Kozlowski
2026-08-05 19:12 ` Leander Kieweg
2026-07-30 17:36 ` [PATCH v2 2/2] drm/glanda: Add initial DRM driver for GlandaGPU Leander Kieweg
2026-07-30 17:52 ` sashiko-bot
2026-07-31 9:56 ` Krzysztof Kozlowski
2026-08-02 16:27 ` Leander Kieweg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox