* [PATCH 0/9] drm: replace simple display pipe users with atomic helpers
@ 2026-07-04 18:31 Ze Huang
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
` (9 more replies)
0 siblings, 10 replies; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
struct drm_simple_display_pipe was meant to simplify simple DRM
drivers, but instead adds an extra wrapper around normal DRM atomic
helper setup. As noted in Documentation/gpu/todo.rst, remaining users
should be converted to regular atomic helpers and stop depending on the
simple-KMS interfaces.
This series converts the following drivers:
- arcpgu
- aspeed
- imx lcdc
- mcde
- pl111
- gm12u320
- repaper
- tve200
- xen frontend
Each patch replaces drm_simple_display_pipe_init() with explicit
primary plane, CRTC and encoder setup, and moves the old simple-pipe
callbacks into regular plane and CRTC helper callbacks named according
to local driver conventions.
The conversions preserve helper behavior that used to be implicit in
drm_simple_kms_helper.c, including plane-state validation, CRTC
primary-plane checks, affected-plane propagation, framebuffer prepare
handling, and existing event/vblank flow where applicable.
Result is less helper indirection and more explicit driver-side atomic
wiring, with no remaining simple-KMS dependency in these drivers.
These changes are build-tested only. No hardware testing has been
performed on the affected devices.
This series is based on drm-next-2026-06-27.
Thanks,
Ze Huang
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
Ze Huang (9):
drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
drm/imx: replace struct drm_simple_display_pipe with regular atomic helpers
drm/mcde: replace struct drm_simple_display_pipe with regular atomic helpers
drm/pl111: replace struct drm_simple_display_pipe with regular atomic helpers
drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
drm/repaper: replace struct drm_simple_display_pipe with regular atomic helpers
drm/tve200: replace struct drm_simple_display_pipe with regular atomic helpers
drm/xen: replace struct drm_simple_display_pipe with regular atomic helpers
drivers/gpu/drm/aspeed/aspeed_gfx.h | 5 +-
drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 156 ++++++++++++++++------
drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 3 +-
drivers/gpu/drm/imx/lcdc/imx-lcdc.c | 178 ++++++++++++++++++-------
drivers/gpu/drm/mcde/mcde_display.c | 162 ++++++++++++++++------
drivers/gpu/drm/mcde/mcde_drm.h | 6 +-
drivers/gpu/drm/mcde/mcde_drv.c | 3 +-
drivers/gpu/drm/pl111/pl111_display.c | 174 ++++++++++++++++++------
drivers/gpu/drm/pl111/pl111_drm.h | 5 +-
drivers/gpu/drm/pl111/pl111_drv.c | 3 +-
drivers/gpu/drm/tiny/arcpgu.c | 165 ++++++++++++++++++-----
drivers/gpu/drm/tiny/gm12u320.c | 128 ++++++++++++++----
drivers/gpu/drm/tiny/repaper.c | 130 ++++++++++++++----
drivers/gpu/drm/tve200/tve200_display.c | 221 +++++++++++++++++++++----------
drivers/gpu/drm/tve200/tve200_drm.h | 6 +-
drivers/gpu/drm/tve200/tve200_drv.c | 17 ++-
drivers/gpu/drm/xen/xen_drm_front.h | 6 +-
drivers/gpu/drm/xen/xen_drm_front_kms.c | 177 +++++++++++++++++++------
18 files changed, 1164 insertions(+), 381 deletions(-)
---
base-commit: 3696d07837d1df13a5603d77f667685e7dfb3c53
change-id: 20260704-drm-simple-kms-removal-01a031c6a129
Best regards,
--
Ze Huang <ze.huang@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:43 ` sashiko-bot
2026-07-06 8:01 ` Thomas Zimmermann
2026-07-04 18:31 ` [PATCH 2/9] drm/aspeed: " Ze Huang
` (8 subsequent siblings)
9 siblings, 2 replies; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Instantiate plane, CRTC and encoder directly and wire them up with
standard atomic helpers.
This removes arcpgu's dependency on deprecated simple-KMS display pipe
interface.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/tiny/arcpgu.c | 165 +++++++++++++++++++++++++++++++++---------
1 file changed, 131 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c
index c93d61ac0bb7..375cdb79e4e8 100644
--- a/drivers/gpu/drm/tiny/arcpgu.c
+++ b/drivers/gpu/drm/tiny/arcpgu.c
@@ -17,12 +17,12 @@
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_reserved_mem.h>
@@ -52,14 +52,14 @@ struct arcpgu_drm_private {
struct drm_device drm;
void __iomem *regs;
struct clk *clk;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector sim_conn;
};
#define dev_to_arcpgu(x) container_of(x, struct arcpgu_drm_private, drm)
-#define pipe_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, pipe)
-
static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu,
unsigned int reg, u32 value)
{
@@ -117,7 +117,7 @@ static const u32 arc_pgu_supported_formats[] = {
static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
{
- const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb;
+ const struct drm_framebuffer *fb = arcpgu->plane.state->fb;
uint32_t pixel_format = fb->format->format;
u32 format = DRM_FORMAT_INVALID;
int i;
@@ -139,10 +139,10 @@ static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl);
}
-static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *pipe,
- const struct drm_display_mode *mode)
+static enum drm_mode_status arcpgu_crtc_helper_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
{
- struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
+ struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
long rate, clk_rate = mode->clock * 1000;
long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */
@@ -155,7 +155,7 @@ static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *p
static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
{
- struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode;
+ struct drm_display_mode *m = &arcpgu->crtc.state->adjusted_mode;
u32 val;
arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
@@ -194,11 +194,10 @@ static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
}
-static void arc_pgu_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void arcpgu_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
+ struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
arc_pgu_mode_set(arcpgu);
@@ -208,9 +207,10 @@ static void arc_pgu_enable(struct drm_simple_display_pipe *pipe,
ARCPGU_CTRL_ENABLE_MASK);
}
-static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
+static void arcpgu_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
+ struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
clk_disable_unprepare(arcpgu->clk);
arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
@@ -218,35 +218,106 @@ static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
~ARCPGU_CTRL_ENABLE_MASK);
}
-static void arc_pgu_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *state)
+static void arcpgu_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
struct arcpgu_drm_private *arcpgu;
struct drm_gem_dma_object *gem;
- if (!pipe->plane.state->fb)
+ if (!plane->state->fb)
return;
- arcpgu = pipe_to_arcpgu_priv(pipe);
- gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
+ arcpgu = dev_to_arcpgu(plane->dev);
+ gem = drm_fb_dma_get_gem_obj(plane->state->fb, 0);
arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->dma_addr);
}
-static const struct drm_simple_display_pipe_funcs arc_pgu_pipe_funcs = {
- .update = arc_pgu_update,
- .mode_valid = arc_pgu_mode_valid,
- .enable = arc_pgu_enable,
- .disable = arc_pgu_disable,
-};
-
static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
- .fb_create = drm_gem_fb_create,
+ .fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
DEFINE_DRM_GEM_DMA_FOPS(arcpgu_drm_ops);
+static int arcpgu_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ return ret;
+}
+
+static const struct drm_plane_helper_funcs arcpgu_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = arcpgu_plane_helper_atomic_check,
+ .atomic_update = arcpgu_plane_helper_atomic_update,
+};
+
+static bool arcpgu_plane_format_mod_supported(struct drm_plane *plane,
+ u32 format,
+ u64 modifier)
+{
+ return modifier == DRM_FORMAT_MOD_LINEAR;
+}
+
+static const struct drm_plane_funcs arcpgu_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,
+ .format_mod_supported = arcpgu_plane_format_mod_supported,
+};
+
+static int arcpgu_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_helper_funcs arcpgu_crtc_helper_funcs = {
+ .mode_valid = arcpgu_crtc_helper_mode_valid,
+ .atomic_check = arcpgu_crtc_helper_atomic_check,
+ .atomic_enable = arcpgu_crtc_helper_atomic_enable,
+ .atomic_disable = arcpgu_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs arcpgu_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs arcpgu_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
{
struct platform_device *pdev = to_platform_device(arcpgu->drm.dev);
@@ -254,6 +325,9 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
struct device_node *endpoint_node = NULL;
struct drm_connector *connector = NULL;
struct drm_device *drm = &arcpgu->drm;
+ struct drm_plane *plane;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
int ret;
arcpgu->clk = devm_clk_get(drm->dev, "pxlclk");
@@ -301,12 +375,35 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
return ret;
}
- ret = drm_simple_display_pipe_init(drm, &arcpgu->pipe, &arc_pgu_pipe_funcs,
- arc_pgu_supported_formats,
- ARRAY_SIZE(arc_pgu_supported_formats),
- NULL, connector);
+ plane = &arcpgu->plane;
+ ret = drm_universal_plane_init(drm, plane, 0,
+ &arcpgu_plane_funcs,
+ arc_pgu_supported_formats,
+ ARRAY_SIZE(arc_pgu_supported_formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
+ drm_plane_helper_add(plane, &arcpgu_plane_helper_funcs);
+
+ crtc = &arcpgu->crtc;
+ ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
+ &arcpgu_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+ drm_crtc_helper_add(crtc, &arcpgu_crtc_helper_funcs);
+
+ encoder = &arcpgu->encoder;
+ ret = drm_encoder_init(drm, encoder, &arcpgu_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ if (connector) {
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return ret;
+ }
if (encoder_node) {
/* Locate drm bridge from the hdmi encoder DT node */
@@ -315,7 +412,7 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
if (!bridge)
return -EPROBE_DEFER;
- ret = drm_simple_display_pipe_attach_bridge(&arcpgu->pipe, bridge);
+ ret = drm_bridge_attach(encoder, bridge, NULL, 0);
if (ret)
return ret;
}
@@ -342,7 +439,7 @@ static int arcpgu_show_pxlclock(struct seq_file *m, void *arg)
struct drm_device *drm = node->minor->dev;
struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(drm);
unsigned long clkrate = clk_get_rate(arcpgu->clk);
- unsigned long mode_clock = arcpgu->pipe.crtc.mode.crtc_clock * 1000;
+ unsigned long mode_clock = arcpgu->crtc.mode.crtc_clock * 1000;
seq_printf(m, "hw : %lu\n", clkrate);
seq_printf(m, "mode: %lu\n", mode_clock);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 2/9] drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:52 ` sashiko-bot
2026-07-06 8:31 ` Thomas Zimmermann
2026-07-04 18:31 ` [PATCH 3/9] drm/imx: " Ze Huang
` (7 subsequent siblings)
9 siblings, 2 replies; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Replace simple display pipe with explicit plane, CRTC and encoder
objects. Move callbacks to plane and CRTC helpers, with vblank handling
through drm_crtc_funcs.
This removes intermediate simple-pipe layer and uses standard atomic
helper wiring.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/aspeed/aspeed_gfx.h | 5 +-
drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 156 +++++++++++++++++++++++--------
drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 3 +-
3 files changed, 123 insertions(+), 41 deletions(-)
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h
index 4e6a442c3886..a34811564c0d 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx.h
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h
@@ -2,7 +2,6 @@
/* Copyright 2018 IBM Corporation */
#include <drm/drm_device.h>
-#include <drm/drm_simple_kms_helper.h>
struct aspeed_gfx {
struct drm_device drm;
@@ -17,7 +16,9 @@ struct aspeed_gfx {
u32 throd_val;
u32 scan_line_max;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector connector;
};
#define to_aspeed_gfx(x) container_of(x, struct aspeed_gfx, drm)
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
index 7877a57b8e26..3294795c31c4 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
@@ -5,6 +5,8 @@
#include <linux/reset.h>
#include <linux/regmap.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
@@ -12,20 +14,13 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_panel.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include "aspeed_gfx.h"
-static struct aspeed_gfx *
-drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
-{
- return container_of(pipe, struct aspeed_gfx, pipe);
-}
-
static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
{
- struct drm_crtc *crtc = &priv->pipe.crtc;
+ struct drm_crtc *crtc = &priv->crtc;
struct drm_device *drm = crtc->dev;
const u32 format = crtc->primary->state->fb->format->format;
u32 ctrl1;
@@ -79,7 +74,7 @@ static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
{
- struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
+ struct drm_display_mode *m = &priv->crtc.state->adjusted_mode;
u32 ctrl1, d_offset, t_count, bpp;
int err;
@@ -139,33 +134,31 @@ static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
writel(priv->throd_val, priv->base + CRT_THROD);
}
-static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void aspeed_gfx_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
- struct drm_crtc *crtc = &pipe->crtc;
+ struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
aspeed_gfx_crtc_mode_set_nofb(priv);
aspeed_gfx_enable_controller(priv);
drm_crtc_vblank_on(crtc);
}
-static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
+static void aspeed_gfx_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
- struct drm_crtc *crtc = &pipe->crtc;
+ struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
drm_crtc_vblank_off(crtc);
aspeed_gfx_disable_controller(priv);
}
-static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *plane_state)
+static void aspeed_gfx_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_framebuffer *fb = pipe->plane.state->fb;
+ struct aspeed_gfx *priv = container_of(plane, struct aspeed_gfx, plane);
+ struct drm_crtc *crtc = &priv->crtc;
+ struct drm_framebuffer *fb = plane->state->fb;
struct drm_pending_vblank_event *event;
struct drm_gem_dma_object *gem;
@@ -190,9 +183,9 @@ static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
writel(gem->dma_addr, priv->base + CRT_ADDR);
}
-static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
+static int aspeed_gfx_crtc_enable_vblank(struct drm_crtc *crtc)
{
- struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+ struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
u32 reg = readl(priv->base + CRT_CTRL1);
/* Clear pending VBLANK IRQ */
@@ -204,9 +197,9 @@ static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
return 0;
}
-static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
+static void aspeed_gfx_crtc_disable_vblank(struct drm_crtc *crtc)
{
- struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
+ struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
u32 reg = readl(priv->base + CRT_CTRL1);
reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
@@ -216,12 +209,75 @@ static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
}
-static const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
- .enable = aspeed_gfx_pipe_enable,
- .disable = aspeed_gfx_pipe_disable,
- .update = aspeed_gfx_pipe_update,
- .enable_vblank = aspeed_gfx_enable_vblank,
- .disable_vblank = aspeed_gfx_disable_vblank,
+static int aspeed_gfx_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ return ret;
+}
+
+static const struct drm_plane_helper_funcs aspeed_gfx_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = aspeed_gfx_plane_helper_atomic_check,
+ .atomic_update = aspeed_gfx_plane_helper_atomic_update,
+};
+
+static const struct drm_plane_funcs aspeed_gfx_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 aspeed_gfx_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_helper_funcs aspeed_gfx_crtc_helper_funcs = {
+ .atomic_check = aspeed_gfx_crtc_helper_atomic_check,
+ .atomic_enable = aspeed_gfx_crtc_helper_atomic_enable,
+ .atomic_disable = aspeed_gfx_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs aspeed_gfx_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = aspeed_gfx_crtc_enable_vblank,
+ .disable_vblank = aspeed_gfx_crtc_disable_vblank,
+};
+
+static const struct drm_encoder_funcs aspeed_gfx_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static const uint32_t aspeed_gfx_formats[] = {
@@ -232,10 +288,36 @@ static const uint32_t aspeed_gfx_formats[] = {
int aspeed_gfx_create_pipe(struct drm_device *drm)
{
struct aspeed_gfx *priv = to_aspeed_gfx(drm);
+ struct drm_plane *plane = &priv->plane;
+ struct drm_crtc *crtc = &priv->crtc;
+ struct drm_encoder *encoder = &priv->encoder;
+ int ret;
+
+ ret = drm_universal_plane_init(drm, plane, 0,
+ &aspeed_gfx_plane_funcs,
+ aspeed_gfx_formats,
+ ARRAY_SIZE(aspeed_gfx_formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+ drm_plane_helper_add(plane, &aspeed_gfx_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
+ &aspeed_gfx_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+ drm_crtc_helper_add(crtc, &aspeed_gfx_crtc_helper_funcs);
+
+ ret = drm_encoder_init(drm, encoder, &aspeed_gfx_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ ret = drm_connector_attach_encoder(&priv->connector, encoder);
+ if (ret)
+ return ret;
- return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
- aspeed_gfx_formats,
- ARRAY_SIZE(aspeed_gfx_formats),
- NULL,
- &priv->connector);
+ return 0;
}
diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
index 46094cca2974..b2d805f0c16d 100644
--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
@@ -21,7 +21,6 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_drv.h>
@@ -130,7 +129,7 @@ static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
reg = readl(priv->base + CRT_CTRL1);
if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
- drm_crtc_handle_vblank(&priv->pipe.crtc);
+ drm_crtc_handle_vblank(&priv->crtc);
writel(reg, priv->base + priv->int_clr_reg);
return IRQ_HANDLED;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 3/9] drm/imx: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
2026-07-04 18:31 ` [PATCH 2/9] drm/aspeed: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:46 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 4/9] drm/mcde: " Ze Huang
` (6 subsequent siblings)
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Convert i.MX LCDC to explicit primary plane, CRTC and encoder objects.
Keep no-scaling plane check and GEM framebuffer prepare callback from
simple-KMS path. Only touch hardware state when framebuffer exists,
since atomic plane updates can run on disabling transitions.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/imx/lcdc/imx-lcdc.c | 178 ++++++++++++++++++++++++++----------
1 file changed, 130 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
index f52832b43aca..d091dc562098 100644
--- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
+++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
@@ -14,9 +14,9 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_plane_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
@@ -102,7 +102,9 @@
struct imx_lcdc {
struct drm_device drm;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector *connector;
void __iomem *base;
@@ -135,14 +137,13 @@ static unsigned int imx_lcdc_get_format(unsigned int drm_format)
}
}
-static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
+static void imx_lcdc_update_hw_registers(struct drm_crtc *crtc,
struct drm_plane_state *old_state,
bool mode_set)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_plane_state *new_state = pipe->plane.state;
+ struct drm_plane_state *new_state = crtc->primary->state;
struct drm_framebuffer *fb = new_state->fb;
- struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
+ struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
u32 lpcr, lvcr, lhcr;
u32 framesize;
dma_addr_t addr;
@@ -188,16 +189,16 @@ static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
clk_prepare_enable(lcdc->clk_per);
}
-static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void imx_lcdc_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
int ret;
int clk_div;
int bpp;
- struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
- struct drm_display_mode *mode = &pipe->crtc.mode;
+ struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
+ struct drm_display_mode *mode = &crtc->mode;
struct drm_display_info *disp_info = &lcdc->connector->display_info;
+ struct drm_plane_state *plane_state = crtc->primary->state;
const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1;
const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1;
const int data_enable_pol =
@@ -231,34 +232,34 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
ret = clk_prepare_enable(lcdc->clk_ipg);
if (ret) {
- dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
+ dev_err(crtc->dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
return;
}
ret = clk_prepare_enable(lcdc->clk_ahb);
if (ret) {
- dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
+ dev_err(crtc->dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
clk_disable_unprepare(lcdc->clk_ipg);
return;
}
- imx_lcdc_update_hw_registers(pipe, NULL, true);
+ imx_lcdc_update_hw_registers(crtc, NULL, true);
/* Enable VBLANK Interrupt */
writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER);
}
-static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
+static void imx_lcdc_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
- struct drm_crtc *crtc = &lcdc->pipe.crtc;
+ struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
struct drm_pending_vblank_event *event;
clk_disable_unprepare(lcdc->clk_ahb);
clk_disable_unprepare(lcdc->clk_ipg);
- if (pipe->crtc.enabled)
+ if (crtc->enabled)
clk_disable_unprepare(lcdc->clk_per);
spin_lock_irq(&lcdc->drm.event_lock);
@@ -273,17 +274,18 @@ static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
writel(0, lcdc->base + IMX21LCDC_LIER);
}
-static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *plane_state,
- struct drm_crtc_state *crtc_state)
+static int imx_lcdc_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
const struct drm_display_mode *mode = &crtc_state->mode;
- const struct drm_display_mode *old_mode = &pipe->crtc.state->mode;
+ const struct drm_display_mode *old_mode = &crtc->state->mode;
+ int ret;
if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
mode->hdisplay % 0x10) { /* must be multiple of 16 */
- drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n",
+ drm_err(crtc->dev, "unsupported display mode (%u x %u)\n",
mode->hdisplay, mode->vdisplay);
return -EINVAL;
}
@@ -292,27 +294,42 @@ static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
old_mode->hdisplay != mode->hdisplay ||
old_mode->vdisplay != mode->vdisplay;
- return 0;
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
}
-static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_state)
+static void imx_lcdc_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_pending_vblank_event *event = crtc->state->event;
- struct drm_plane_state *new_state = pipe->plane.state;
+ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+ struct drm_plane_state *new_state = plane->state;
+ struct drm_crtc *crtc = new_state->crtc ?: old_state->crtc;
+ struct drm_pending_vblank_event *event;
struct drm_framebuffer *fb = new_state->fb;
struct drm_framebuffer *old_fb = old_state->fb;
struct drm_crtc *old_crtc = old_state->crtc;
bool mode_changed = false;
- if (old_fb && old_fb->format != fb->format)
- mode_changed = true;
- else if (old_crtc != crtc)
- mode_changed = true;
+ if (!crtc)
+ return;
- imx_lcdc_update_hw_registers(pipe, old_state, mode_changed);
+ if (fb) {
+ if (old_fb && old_fb->format != fb->format)
+ mode_changed = true;
+ else if (old_crtc != crtc)
+ mode_changed = true;
+ imx_lcdc_update_hw_registers(crtc, old_state, mode_changed);
+ }
+
+ event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
@@ -327,11 +344,56 @@ static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
}
}
-static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = {
- .enable = imx_lcdc_pipe_enable,
- .disable = imx_lcdc_pipe_disable,
- .check = imx_lcdc_pipe_check,
- .update = imx_lcdc_pipe_update,
+static int imx_lcdc_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ return ret;
+}
+
+static const struct drm_plane_helper_funcs imx_lcdc_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = imx_lcdc_plane_helper_atomic_check,
+ .atomic_update = imx_lcdc_plane_helper_atomic_update,
+};
+
+static const struct drm_plane_funcs imx_lcdc_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 const struct drm_crtc_helper_funcs imx_lcdc_crtc_helper_funcs = {
+ .atomic_check = imx_lcdc_crtc_helper_atomic_check,
+ .atomic_enable = imx_lcdc_crtc_helper_atomic_enable,
+ .atomic_disable = imx_lcdc_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs imx_lcdc_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs imx_lcdc_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = {
@@ -369,7 +431,7 @@ MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id);
static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg)
{
struct imx_lcdc *lcdc = arg;
- struct drm_crtc *crtc = &lcdc->pipe.crtc;
+ struct drm_crtc *crtc = &lcdc->crtc;
unsigned int status;
status = readl(lcdc->base + IMX21LCDC_LISR);
@@ -387,6 +449,9 @@ static int imx_lcdc_probe(struct platform_device *pdev)
struct imx_lcdc *lcdc;
struct drm_device *drm;
struct drm_bridge *bridge;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
int irq;
int ret;
struct device *dev = &pdev->dev;
@@ -428,23 +493,40 @@ static int imx_lcdc_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n");
- /* CRTC, Plane, Encoder */
- ret = drm_simple_display_pipe_init(drm, &lcdc->pipe,
- &imx_lcdc_pipe_funcs,
- imx_lcdc_formats,
- ARRAY_SIZE(imx_lcdc_formats), NULL, NULL);
+ plane = &lcdc->plane;
+ ret = drm_universal_plane_init(drm, plane, 0,
+ &imx_lcdc_plane_funcs,
+ imx_lcdc_formats,
+ ARRAY_SIZE(imx_lcdc_formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret < 0)
+ return dev_err_probe(drm->dev, ret, "Cannot initialize primary plane\n");
+ drm_plane_helper_add(plane, &imx_lcdc_plane_helper_funcs);
+
+ crtc = &lcdc->crtc;
+ ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
+ &imx_lcdc_crtc_funcs, NULL);
+ if (ret < 0)
+ return dev_err_probe(drm->dev, ret, "Cannot initialize CRTC\n");
+ drm_crtc_helper_add(crtc, &imx_lcdc_crtc_helper_funcs);
+
+ encoder = &lcdc->encoder;
+ ret = drm_encoder_init(drm, encoder, &imx_lcdc_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
if (ret < 0)
- return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n");
+ return dev_err_probe(drm->dev, ret, "Cannot initialize encoder\n");
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n");
- ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n");
- lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder);
+ lcdc->connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(lcdc->connector))
return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n");
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 4/9] drm/mcde: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (2 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 3/9] drm/imx: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:44 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 5/9] drm/pl111: " Ze Huang
` (5 subsequent siblings)
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Convert MCDE to explicit plane, CRTC and encoder objects.
Keep FIFO, event and framebuffer update sequencing intact, and install
GEM framebuffer prepare callback explicitly.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/mcde/mcde_display.c | 162 +++++++++++++++++++++++++++---------
drivers/gpu/drm/mcde/mcde_drm.h | 6 +-
drivers/gpu/drm/mcde/mcde_drv.c | 3 +-
3 files changed, 129 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index 257a6e84dd58..4d86fa5030eb 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -10,6 +10,7 @@
#include <linux/regulator/consumer.h>
#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
@@ -18,7 +19,6 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_vblank.h>
#include <video/mipi_display.h>
@@ -132,7 +132,7 @@ void mcde_display_irq(struct mcde *mcde)
writel(mispp, mcde->regs + MCDE_RISPP);
if (vblank)
- drm_crtc_handle_vblank(&mcde->pipe.crtc);
+ drm_crtc_handle_vblank(&mcde->crtc);
if (misovl)
dev_info(mcde->dev, "some stray overlay IRQ %08x\n", misovl);
@@ -157,13 +157,35 @@ void mcde_display_disable_irqs(struct mcde *mcde)
writel(0xFFFFFFFF, mcde->regs + MCDE_RISCHNL);
}
-static int mcde_display_check(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *pstate,
- struct drm_crtc_state *cstate)
+static int mcde_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+ struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = pstate->crtc;
+ struct drm_crtc_state *cstate;
+ const struct drm_display_mode *mode;
+ struct drm_framebuffer *old_fb = plane->state->fb;
struct drm_framebuffer *fb = pstate->fb;
+ int ret;
+
+ if (!crtc)
+ return 0;
+
+ cstate = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!cstate)
+ return 0;
+
+ ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+
+ if (!pstate->visible)
+ return 0;
+
+ mode = &cstate->mode;
if (fb) {
u32 offset = drm_fb_dma_get_gem_addr(fb, pstate, 0);
@@ -1149,16 +1171,14 @@ static void mcde_setup_dsi(struct mcde *mcde, const struct drm_display_mode *mod
*dsi_formatter_frame = formatter_frame;
}
-static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *cstate,
- struct drm_plane_state *plane_state)
+static void mcde_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_plane *plane = &pipe->plane;
struct drm_device *drm = crtc->dev;
struct mcde *mcde = to_mcde(drm);
+ struct drm_crtc_state *cstate = crtc->state;
const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *fb = plane->state->fb;
+ struct drm_framebuffer *fb = mcde->plane.state->fb;
u32 format = fb->format->format;
int dsi_pkt_size;
int fifo_wtrmrk;
@@ -1298,9 +1318,9 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
dev_info(drm->dev, "MCDE display is enabled\n");
}
-static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
+static void mcde_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct mcde *mcde = to_mcde(drm);
struct drm_pending_vblank_event *event;
@@ -1381,17 +1401,23 @@ static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address)
writel(buffer_address + mcde->stride, mcde->regs + MCDE_EXTSRCXA1);
}
-static void mcde_display_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_pstate)
+static void mcde_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_device *drm = crtc->dev;
- struct mcde *mcde = to_mcde(drm);
- struct drm_pending_vblank_event *event = crtc->state->event;
- struct drm_plane *plane = &pipe->plane;
+ struct drm_crtc *crtc = plane->state->crtc;
+ struct drm_device *drm;
+ struct mcde *mcde;
+ struct drm_pending_vblank_event *event;
struct drm_plane_state *pstate = plane->state;
struct drm_framebuffer *fb = pstate->fb;
+ if (!crtc)
+ return;
+
+ drm = crtc->dev;
+ mcde = to_mcde(drm);
+ event = crtc->state->event;
+
/*
* Handle any pending event first, we need to arm the vblank
* interrupt before sending any update to the display so we don't
@@ -1443,9 +1469,8 @@ static void mcde_display_update(struct drm_simple_display_pipe *pipe,
}
}
-static int mcde_display_enable_vblank(struct drm_simple_display_pipe *pipe)
+static int mcde_crtc_enable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct mcde *mcde = to_mcde(drm);
u32 val;
@@ -1462,9 +1487,8 @@ static int mcde_display_enable_vblank(struct drm_simple_display_pipe *pipe)
return 0;
}
-static void mcde_display_disable_vblank(struct drm_simple_display_pipe *pipe)
+static void mcde_crtc_disable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct mcde *mcde = to_mcde(drm);
@@ -1474,13 +1498,56 @@ static void mcde_display_disable_vblank(struct drm_simple_display_pipe *pipe)
writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP);
}
-static struct drm_simple_display_pipe_funcs mcde_display_funcs = {
- .check = mcde_display_check,
- .enable = mcde_display_enable,
- .disable = mcde_display_disable,
- .update = mcde_display_update,
- .enable_vblank = mcde_display_enable_vblank,
- .disable_vblank = mcde_display_disable_vblank,
+static int mcde_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_funcs mcde_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = mcde_crtc_enable_vblank,
+ .disable_vblank = mcde_crtc_disable_vblank,
+};
+
+static const struct drm_crtc_helper_funcs mcde_crtc_helper_funcs = {
+ .atomic_check = mcde_crtc_helper_atomic_check,
+ .atomic_enable = mcde_crtc_helper_atomic_enable,
+ .atomic_disable = mcde_crtc_helper_atomic_disable,
+};
+
+static const struct drm_plane_funcs mcde_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .destroy = drm_plane_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs mcde_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = mcde_plane_helper_atomic_check,
+ .atomic_update = mcde_plane_helper_atomic_update,
+};
+
+static const struct drm_encoder_funcs mcde_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
int mcde_display_init(struct drm_device *drm)
@@ -1510,11 +1577,30 @@ int mcde_display_init(struct drm_device *drm)
if (ret)
return ret;
- ret = drm_simple_display_pipe_init(drm, &mcde->pipe,
- &mcde_display_funcs,
- formats, ARRAY_SIZE(formats),
- NULL,
- mcde->connector);
+ ret = drm_universal_plane_init(drm, &mcde->plane, 0,
+ &mcde_plane_funcs,
+ formats, ARRAY_SIZE(formats),
+ NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+
+ drm_plane_helper_add(&mcde->plane, &mcde_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, &mcde->crtc, &mcde->plane,
+ NULL, &mcde_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(&mcde->crtc, &mcde_crtc_helper_funcs);
+
+ ret = drm_encoder_init(drm, &mcde->encoder, &mcde_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ mcde->encoder.possible_crtcs = drm_crtc_mask(&mcde->crtc);
+
+ ret = drm_connector_attach_encoder(mcde->connector, &mcde->encoder);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h
index ecb70b4b737c..6123afb1e3b8 100644
--- a/drivers/gpu/drm/mcde/mcde_drm.h
+++ b/drivers/gpu/drm/mcde/mcde_drm.h
@@ -4,7 +4,7 @@
* Parts of this file were based on the MCDE driver by Marcus Lorentzon
* (C) ST-Ericsson SA 2013
*/
-#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_encoder.h>
#ifndef _MCDE_DRM_H_
#define _MCDE_DRM_H_
@@ -72,7 +72,9 @@ struct mcde {
struct drm_panel *panel;
struct drm_bridge *bridge;
struct drm_connector *connector;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct mipi_dsi_device *mdsi;
bool dpi_output;
s16 stride;
diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c
index 5f2c462bad7e..401cf8ab83bc 100644
--- a/drivers/gpu/drm/mcde/mcde_drv.c
+++ b/drivers/gpu/drm/mcde/mcde_drv.c
@@ -186,8 +186,7 @@ static int mcde_modeset_init(struct drm_device *drm)
}
/* Attach the bridge. */
- ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe,
- mcde->bridge);
+ ret = drm_bridge_attach(&mcde->encoder, mcde->bridge, NULL, 0);
if (ret) {
dev_err(drm->dev, "failed to attach display output bridge\n");
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 5/9] drm/pl111: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (3 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 4/9] drm/mcde: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:45 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 6/9] drm/gm12u320: " Ze Huang
` (4 subsequent siblings)
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Replace PL111 simple display pipe with explicit plane, CRTC and encoder
objects.
Keep existing hardware programming and vblank behavior, and install GEM
framebuffer prepare helper explicitly.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/pl111/pl111_display.c | 174 ++++++++++++++++++++++++++--------
drivers/gpu/drm/pl111/pl111_drm.h | 5 +-
drivers/gpu/drm/pl111/pl111_drv.c | 3 +-
3 files changed, 136 insertions(+), 46 deletions(-)
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
index 5d10bc5fdf1f..b1bdd4c9dbe6 100644
--- a/drivers/gpu/drm/pl111/pl111_display.c
+++ b/drivers/gpu/drm/pl111/pl111_display.c
@@ -15,6 +15,7 @@
#include <linux/media-bus-format.h>
#include <linux/of_graph.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@@ -37,7 +38,7 @@ irqreturn_t pl111_irq(int irq, void *data)
return IRQ_NONE;
if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
- drm_crtc_handle_vblank(&priv->pipe.crtc);
+ drm_crtc_handle_vblank(&priv->crtc);
status = IRQ_HANDLED;
}
@@ -49,10 +50,10 @@ irqreturn_t pl111_irq(int irq, void *data)
}
static enum drm_mode_status
-pl111_mode_valid(struct drm_simple_display_pipe *pipe,
- const struct drm_display_mode *mode)
+pl111_crtc_helper_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
{
- struct drm_device *drm = pipe->crtc.dev;
+ struct drm_device *drm = crtc->dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
u32 cpp = DIV_ROUND_UP(priv->variant->fb_depth, 8);
u64 bw;
@@ -83,13 +84,35 @@ pl111_mode_valid(struct drm_simple_display_pipe *pipe,
return MODE_OK;
}
-static int pl111_display_check(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *pstate,
- struct drm_crtc_state *cstate)
+static int pl111_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+ struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = pstate->crtc;
+ struct drm_crtc_state *cstate;
+ const struct drm_display_mode *mode;
+ struct drm_framebuffer *old_fb = plane->state->fb;
struct drm_framebuffer *fb = pstate->fb;
+ int ret;
+
+ if (!crtc)
+ return 0;
+
+ cstate = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!cstate)
+ return 0;
+
+ ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+
+ if (!pstate->visible)
+ return 0;
+
+ mode = &cstate->mode;
if (mode->hdisplay % 16)
return -EINVAL;
@@ -117,16 +140,14 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
return 0;
}
-static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *cstate,
- struct drm_plane_state *plane_state)
+static void pl111_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_plane *plane = &pipe->plane;
struct drm_device *drm = crtc->dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
+ struct drm_crtc_state *cstate = crtc->state;
const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *fb = plane->state->fb;
+ struct drm_framebuffer *fb = priv->plane.state->fb;
struct drm_connector *connector = priv->connector;
struct drm_bridge *bridge = priv->bridge;
bool grayscale = false;
@@ -355,9 +376,9 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
drm_crtc_vblank_on(crtc);
}
-static void pl111_display_disable(struct drm_simple_display_pipe *pipe)
+static void pl111_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
u32 cntl;
@@ -387,17 +408,23 @@ static void pl111_display_disable(struct drm_simple_display_pipe *pipe)
clk_disable_unprepare(priv->clk);
}
-static void pl111_display_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_pstate)
+static void pl111_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_device *drm = crtc->dev;
- struct pl111_drm_dev_private *priv = drm->dev_private;
- struct drm_pending_vblank_event *event = crtc->state->event;
- struct drm_plane *plane = &pipe->plane;
+ struct drm_crtc *crtc = plane->state->crtc;
+ struct drm_device *drm;
+ struct pl111_drm_dev_private *priv;
+ struct drm_pending_vblank_event *event;
struct drm_plane_state *pstate = plane->state;
struct drm_framebuffer *fb = pstate->fb;
+ if (!crtc)
+ return;
+
+ drm = crtc->dev;
+ priv = drm->dev_private;
+ event = crtc->state->event;
+
if (fb) {
u32 addr = drm_fb_dma_get_gem_addr(fb, pstate, 0);
@@ -416,9 +443,8 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe,
}
}
-static int pl111_display_enable_vblank(struct drm_simple_display_pipe *pipe)
+static int pl111_display_enable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
@@ -427,21 +453,63 @@ static int pl111_display_enable_vblank(struct drm_simple_display_pipe *pipe)
return 0;
}
-static void pl111_display_disable_vblank(struct drm_simple_display_pipe *pipe)
+static void pl111_display_disable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct pl111_drm_dev_private *priv = drm->dev_private;
writel(0, priv->regs + priv->ienb);
}
-static struct drm_simple_display_pipe_funcs pl111_display_funcs = {
- .mode_valid = pl111_mode_valid,
- .check = pl111_display_check,
- .enable = pl111_display_enable,
- .disable = pl111_display_disable,
- .update = pl111_display_update,
+static int pl111_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static struct drm_crtc_funcs pl111_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs pl111_crtc_helper_funcs = {
+ .mode_valid = pl111_crtc_helper_mode_valid,
+ .atomic_check = pl111_crtc_helper_atomic_check,
+ .atomic_enable = pl111_crtc_helper_atomic_enable,
+ .atomic_disable = pl111_crtc_helper_atomic_disable,
+};
+
+static const struct drm_plane_funcs pl111_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .destroy = drm_plane_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs pl111_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = pl111_plane_helper_atomic_check,
+ .atomic_update = pl111_plane_helper_atomic_update,
+};
+
+static const struct drm_encoder_funcs pl111_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
@@ -583,18 +651,40 @@ int pl111_display_init(struct drm_device *drm)
return ret;
if (!priv->variant->broken_vblank) {
- pl111_display_funcs.enable_vblank = pl111_display_enable_vblank;
- pl111_display_funcs.disable_vblank = pl111_display_disable_vblank;
+ pl111_crtc_funcs.enable_vblank = pl111_display_enable_vblank;
+ pl111_crtc_funcs.disable_vblank = pl111_display_disable_vblank;
}
- ret = drm_simple_display_pipe_init(drm, &priv->pipe,
- &pl111_display_funcs,
- priv->variant->formats,
- priv->variant->nformats,
- NULL,
- priv->connector);
+ ret = drm_universal_plane_init(drm, &priv->plane, 0,
+ &pl111_plane_funcs,
+ priv->variant->formats,
+ priv->variant->nformats,
+ NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
+ drm_plane_helper_add(&priv->plane, &pl111_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->plane,
+ NULL, &pl111_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(&priv->crtc, &pl111_crtc_helper_funcs);
+
+ ret = drm_encoder_init(drm, &priv->encoder, &pl111_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ priv->encoder.possible_crtcs = drm_crtc_mask(&priv->crtc);
+
+ if (priv->connector) {
+ ret = drm_connector_attach_encoder(priv->connector,
+ &priv->encoder);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
index d1fe756444ee..ec92a5a180a8 100644
--- a/drivers/gpu/drm/pl111/pl111_drm.h
+++ b/drivers/gpu/drm/pl111/pl111_drm.h
@@ -21,7 +21,6 @@
#include <drm/drm_encoder.h>
#include <drm/drm_gem.h>
#include <drm/drm_panel.h>
-#include <drm/drm_simple_kms_helper.h>
/*
* CLCD Controller Internal Register addresses
@@ -135,7 +134,9 @@ struct pl111_drm_dev_private {
struct drm_connector *connector;
struct drm_panel *panel;
struct drm_bridge *bridge;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
void *regs;
u32 memory_bw;
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
index ac7b1d12a0f5..f649c266c33a 100644
--- a/drivers/gpu/drm/pl111/pl111_drv.c
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -168,8 +168,7 @@ static int pl111_modeset_init(struct drm_device *dev)
goto out_bridge;
}
- ret = drm_simple_display_pipe_attach_bridge(&priv->pipe,
- bridge);
+ ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0);
if (ret)
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 6/9] drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (4 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 5/9] drm/pl111: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:45 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 7/9] drm/repaper: " Ze Huang
` (3 subsequent siblings)
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Convert gm12u320 to direct primary plane, CRTC and encoder setup.
Keep shadow-plane helper state, framebuffer access helpers and
no-scaling plane-state check from simple-KMS path.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/tiny/gm12u320.c | 128 ++++++++++++++++++++++++++++++++--------
1 file changed, 104 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index d73dfebb4353..992160ea794d 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -8,6 +8,7 @@
#include <linux/usb.h>
#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
@@ -27,7 +28,6 @@
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
static bool eco_mode;
module_param(eco_mode, bool, 0644);
@@ -87,7 +87,9 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
struct gm12u320_device {
struct drm_device dev;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector conn;
unsigned char *cmd_buf;
unsigned char *data_buf[GM12U320_BLOCK_COUNT];
@@ -554,31 +556,33 @@ static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
}
/* ------------------------------------------------------------------ */
-/* gm12u320 (simple) display pipe */
+/* gm12u320 display pipe */
-static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void gm12u320_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *commit)
{
struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
- struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
+ struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
+ struct drm_plane_state *plane_state = crtc->primary->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
gm12u320_fb_mark_dirty(plane_state->fb, &shadow_plane_state->data[0], &rect);
}
-static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
+static void gm12u320_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *commit)
{
- struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
+ struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
gm12u320_stop_fb_update(gm12u320);
}
-static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_state)
+static void gm12u320_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *commit)
{
- struct drm_plane_state *state = pipe->plane.state;
+ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(commit, plane);
+ struct drm_plane_state *state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_rect rect;
@@ -586,11 +590,71 @@ static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
gm12u320_fb_mark_dirty(state->fb, &shadow_plane_state->data[0], &rect);
}
-static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
- .enable = gm12u320_pipe_enable,
- .disable = gm12u320_pipe_disable,
- .update = gm12u320_pipe_update,
- DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
+static const struct drm_plane_funcs gm12u320_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 gm12u320_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ return ret;
+}
+
+static const struct drm_plane_helper_funcs gm12u320_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = gm12u320_plane_helper_atomic_check,
+ .atomic_update = gm12u320_plane_helper_atomic_update,
+};
+
+static int gm12u320_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_helper_funcs gm12u320_crtc_helper_funcs = {
+ .atomic_check = gm12u320_crtc_helper_atomic_check,
+ .atomic_enable = gm12u320_crtc_helper_atomic_enable,
+ .atomic_disable = gm12u320_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs gm12u320_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs gm12u320_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static const uint32_t gm12u320_pipe_formats[] = {
@@ -677,13 +741,29 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
if (ret)
return ret;
- ret = drm_simple_display_pipe_init(&gm12u320->dev,
- &gm12u320->pipe,
- &gm12u320_pipe_funcs,
- gm12u320_pipe_formats,
- ARRAY_SIZE(gm12u320_pipe_formats),
- gm12u320_pipe_modifiers,
- &gm12u320->conn);
+ ret = drm_universal_plane_init(dev, &gm12u320->plane, 0,
+ &gm12u320_plane_funcs,
+ gm12u320_pipe_formats,
+ ARRAY_SIZE(gm12u320_pipe_formats),
+ gm12u320_pipe_modifiers,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+ drm_plane_helper_add(&gm12u320->plane, &gm12u320_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(dev, &gm12u320->crtc, &gm12u320->plane, NULL,
+ &gm12u320_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+ drm_crtc_helper_add(&gm12u320->crtc, &gm12u320_crtc_helper_funcs);
+
+ ret = drm_encoder_init(dev, &gm12u320->encoder, &gm12u320_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+ gm12u320->encoder.possible_crtcs = drm_crtc_mask(&gm12u320->crtc);
+
+ ret = drm_connector_attach_encoder(&gm12u320->conn, &gm12u320->encoder);
if (ret)
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 7/9] drm/repaper: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (5 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 6/9] drm/gm12u320: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:31 ` [PATCH 8/9] drm/tve200: " Ze Huang
` (2 subsequent siblings)
9 siblings, 0 replies; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Convert repaper to explicit primary plane, CRTC and encoder objects.
Keep shadow-plane helpers, framebuffer access handling and no-scaling
plane-state validation from simple-KMS path.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/tiny/repaper.c | 130 +++++++++++++++++++++++++++++++++--------
1 file changed, 105 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c
index c8270591afc7..23be9c762803 100644
--- a/drivers/gpu/drm/tiny/repaper.c
+++ b/drivers/gpu/drm/tiny/repaper.c
@@ -22,6 +22,7 @@
#include <linux/thermal.h>
#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_damage_helper.h>
@@ -38,7 +39,6 @@
#include <drm/drm_rect.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#define REPAPER_RID_G2_COG_ID 0x12
@@ -65,7 +65,9 @@ enum repaper_epd_border_byte {
struct repaper_epd {
struct drm_device drm;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
const struct drm_display_mode *mode;
struct drm_connector connector;
struct spi_device *spi;
@@ -622,26 +624,24 @@ static void power_off(struct repaper_epd *epd)
gpiod_set_value_cansleep(epd->discharge, 0);
}
-static enum drm_mode_status repaper_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
- const struct drm_display_mode *mode)
+static enum drm_mode_status repaper_crtc_helper_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct repaper_epd *epd = drm_to_epd(crtc->dev);
return drm_crtc_helper_mode_valid_fixed(crtc, mode, epd->mode);
}
-static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void repaper_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *commit)
{
- struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
+ struct repaper_epd *epd = drm_to_epd(crtc->dev);
struct spi_device *spi = epd->spi;
struct device *dev = &spi->dev;
bool dc_ok = false;
int i, ret, idx;
- if (!drm_dev_enter(pipe->crtc.dev, &idx))
+ if (!drm_dev_enter(crtc->dev, &idx))
return;
DRM_DEBUG_DRIVER("\n");
@@ -771,9 +771,10 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
drm_dev_exit(idx);
}
-static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
+static void repaper_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *commit)
{
- struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
+ struct repaper_epd *epd = drm_to_epd(crtc->dev);
struct spi_device *spi = epd->spi;
unsigned int line;
@@ -827,14 +828,15 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
power_off(epd);
}
-static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_state)
+static void repaper_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *commit)
{
- struct drm_plane_state *state = pipe->plane.state;
+ struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(commit, plane);
+ struct drm_plane_state *state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_rect rect;
- if (!pipe->crtc.state->active)
+ if (!state->crtc || !state->crtc->state->active)
return;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
@@ -842,12 +844,72 @@ static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
&shadow_plane_state->fmtcnv_state);
}
-static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
- .mode_valid = repaper_pipe_mode_valid,
- .enable = repaper_pipe_enable,
- .disable = repaper_pipe_disable,
- .update = repaper_pipe_update,
- DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
+static const struct drm_plane_funcs repaper_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 repaper_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ return ret;
+}
+
+static const struct drm_plane_helper_funcs repaper_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = repaper_plane_helper_atomic_check,
+ .atomic_update = repaper_plane_helper_atomic_update,
+};
+
+static int repaper_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_helper_funcs repaper_crtc_helper_funcs = {
+ .mode_valid = repaper_crtc_helper_mode_valid,
+ .atomic_check = repaper_crtc_helper_atomic_check,
+ .atomic_enable = repaper_crtc_helper_atomic_enable,
+ .atomic_disable = repaper_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs repaper_crtc_funcs = {
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs repaper_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static int repaper_connector_get_modes(struct drm_connector *connector)
@@ -1102,9 +1164,27 @@ static int repaper_probe(struct spi_device *spi)
if (ret)
return ret;
- ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
- repaper_formats, ARRAY_SIZE(repaper_formats),
- NULL, &epd->connector);
+ ret = drm_universal_plane_init(drm, &epd->plane, 0,
+ &repaper_plane_funcs,
+ repaper_formats, ARRAY_SIZE(repaper_formats),
+ NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+ drm_plane_helper_add(&epd->plane, &repaper_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, &epd->crtc, &epd->plane, NULL,
+ &repaper_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+ drm_crtc_helper_add(&epd->crtc, &repaper_crtc_helper_funcs);
+
+ ret = drm_encoder_init(drm, &epd->encoder, &repaper_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+ epd->encoder.possible_crtcs = drm_crtc_mask(&epd->crtc);
+
+ ret = drm_connector_attach_encoder(&epd->connector, &epd->encoder);
if (ret)
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 8/9] drm/tve200: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (6 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 7/9] drm/repaper: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:46 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 9/9] drm/xen: " Ze Huang
2026-07-06 7:27 ` [PATCH 0/9] drm: replace simple display pipe users with " Thomas Zimmermann
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Convert TVE200 to explicit plane, CRTC and encoder objects.
Keep generic plane-state validation before TVE200-specific mode,
alignment, pitch and format-change checks, and install GEM framebuffer
prepare helper explicitly.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/tve200/tve200_display.c | 221 ++++++++++++++++++++++----------
drivers/gpu/drm/tve200/tve200_drm.h | 6 +-
drivers/gpu/drm/tve200/tve200_drv.c | 17 ++-
3 files changed, 168 insertions(+), 76 deletions(-)
diff --git a/drivers/gpu/drm/tve200/tve200_display.c b/drivers/gpu/drm/tve200/tve200_display.c
index 26b6c65ef6fd..8fdf69e8e126 100644
--- a/drivers/gpu/drm/tve200/tve200_display.c
+++ b/drivers/gpu/drm/tve200/tve200_display.c
@@ -15,6 +15,8 @@
#include <linux/of_graph.h>
#include <linux/delay.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@@ -52,7 +54,7 @@ irqreturn_t tve200_irq(int irq, void *data)
val = readl(priv->regs + TVE200_CTRL);
/* We have an actual start of vsync */
if (!(val & TVE200_VSTSTYPE_BITS)) {
- drm_crtc_handle_vblank(&priv->pipe.crtc);
+ drm_crtc_handle_vblank(&priv->crtc);
/* Toggle trigger to start of active image */
val |= TVE200_VSTSTYPE_VAI;
} else {
@@ -69,13 +71,34 @@ irqreturn_t tve200_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int tve200_display_check(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *pstate,
- struct drm_crtc_state *cstate)
+static int tve200_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+ struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = pstate->crtc;
+ struct drm_crtc_state *cstate = NULL;
+ const struct drm_display_mode *mode;
+ struct drm_framebuffer *old_fb = plane->state->fb;
struct drm_framebuffer *fb = pstate->fb;
+ int ret;
+
+ if (crtc)
+ cstate = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(pstate, cstate,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+
+ if (!pstate->visible)
+ return 0;
+
+ if (!cstate)
+ return 0;
+
+ mode = &cstate->mode;
/*
* We support these specific resolutions and nothing else.
@@ -119,16 +142,73 @@ static int tve200_display_check(struct drm_simple_display_pipe *pipe,
return 0;
}
-static void tve200_display_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *cstate,
- struct drm_plane_state *plane_state)
+static void tve200_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc *crtc = plane->state->crtc;
+ struct drm_device *drm;
+ struct tve200_drm_dev_private *priv;
+ struct drm_pending_vblank_event *event;
+ struct drm_plane_state *pstate = plane->state;
+ struct drm_framebuffer *fb = pstate->fb;
+
+ if (!crtc)
+ return;
+
+ drm = crtc->dev;
+ priv = drm->dev_private;
+ event = crtc->state->event;
+
+ if (fb) {
+ /* For RGB, the Y component is used as base address */
+ writel(drm_fb_dma_get_gem_addr(fb, pstate, 0),
+ priv->regs + TVE200_Y_FRAME_BASE_ADDR);
+
+ /* For three plane YUV we need two more addresses */
+ if (fb->format->format == DRM_FORMAT_YUV420) {
+ writel(drm_fb_dma_get_gem_addr(fb, pstate, 1),
+ priv->regs + TVE200_U_FRAME_BASE_ADDR);
+ writel(drm_fb_dma_get_gem_addr(fb, pstate, 2),
+ priv->regs + TVE200_V_FRAME_BASE_ADDR);
+ }
+ }
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->active && 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_plane_helper_funcs tve200_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = tve200_plane_helper_atomic_check,
+ .atomic_update = tve200_plane_helper_atomic_update,
+};
+
+static const struct drm_plane_funcs tve200_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 void tve200_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_plane *plane = &pipe->plane;
struct drm_device *drm = crtc->dev;
struct tve200_drm_dev_private *priv = drm->dev_private;
+ struct drm_crtc_state *cstate = drm_atomic_get_new_crtc_state(state, crtc);
const struct drm_display_mode *mode = &cstate->mode;
- struct drm_framebuffer *fb = plane->state->fb;
+ struct drm_plane_state *plane_state = priv->plane.state;
+ struct drm_framebuffer *fb = plane_state->fb;
struct drm_connector *connector = priv->connector;
u32 format = fb->format->format;
u32 ctrl1 = 0;
@@ -240,9 +320,9 @@ static void tve200_display_enable(struct drm_simple_display_pipe *pipe,
drm_crtc_vblank_on(crtc);
}
-static void tve200_display_disable(struct drm_simple_display_pipe *pipe)
+static void tve200_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct tve200_drm_dev_private *priv = drm->dev_private;
@@ -255,46 +335,8 @@ static void tve200_display_disable(struct drm_simple_display_pipe *pipe)
clk_disable_unprepare(priv->clk);
}
-static void tve200_display_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_pstate)
-{
- struct drm_crtc *crtc = &pipe->crtc;
- struct drm_device *drm = crtc->dev;
- struct tve200_drm_dev_private *priv = drm->dev_private;
- struct drm_pending_vblank_event *event = crtc->state->event;
- struct drm_plane *plane = &pipe->plane;
- struct drm_plane_state *pstate = plane->state;
- struct drm_framebuffer *fb = pstate->fb;
-
- if (fb) {
- /* For RGB, the Y component is used as base address */
- writel(drm_fb_dma_get_gem_addr(fb, pstate, 0),
- priv->regs + TVE200_Y_FRAME_BASE_ADDR);
-
- /* For three plane YUV we need two more addresses */
- if (fb->format->format == DRM_FORMAT_YUV420) {
- writel(drm_fb_dma_get_gem_addr(fb, pstate, 1),
- priv->regs + TVE200_U_FRAME_BASE_ADDR);
- writel(drm_fb_dma_get_gem_addr(fb, pstate, 2),
- priv->regs + TVE200_V_FRAME_BASE_ADDR);
- }
- }
-
- if (event) {
- crtc->state->event = NULL;
-
- spin_lock_irq(&crtc->dev->event_lock);
- if (crtc->state->active && 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 int tve200_display_enable_vblank(struct drm_simple_display_pipe *pipe)
+static int tve200_crtc_enable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct tve200_drm_dev_private *priv = drm->dev_private;
@@ -304,22 +346,49 @@ static int tve200_display_enable_vblank(struct drm_simple_display_pipe *pipe)
return 0;
}
-static void tve200_display_disable_vblank(struct drm_simple_display_pipe *pipe)
+static void tve200_crtc_disable_vblank(struct drm_crtc *crtc)
{
- struct drm_crtc *crtc = &pipe->crtc;
struct drm_device *drm = crtc->dev;
struct tve200_drm_dev_private *priv = drm->dev_private;
writel(0, priv->regs + TVE200_INT_EN);
}
-static const struct drm_simple_display_pipe_funcs tve200_display_funcs = {
- .check = tve200_display_check,
- .enable = tve200_display_enable,
- .disable = tve200_display_disable,
- .update = tve200_display_update,
- .enable_vblank = tve200_display_enable_vblank,
- .disable_vblank = tve200_display_disable_vblank,
+static int tve200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_crtc_helper_funcs tve200_crtc_helper_funcs = {
+ .atomic_check = tve200_crtc_helper_atomic_check,
+ .atomic_enable = tve200_crtc_helper_atomic_enable,
+ .atomic_disable = tve200_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs tve200_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = tve200_crtc_enable_vblank,
+ .disable_vblank = tve200_crtc_disable_vblank,
+};
+
+static const struct drm_encoder_funcs tve200_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
int tve200_display_init(struct drm_device *drm)
@@ -346,13 +415,31 @@ int tve200_display_init(struct drm_device *drm)
DRM_FORMAT_YUV420,
};
- ret = drm_simple_display_pipe_init(drm, &priv->pipe,
- &tve200_display_funcs,
- formats, ARRAY_SIZE(formats),
- NULL,
- priv->connector);
+ ret = drm_universal_plane_init(drm, &priv->plane, 0,
+ &tve200_plane_funcs,
+ formats, ARRAY_SIZE(formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+
+ drm_plane_helper_add(&priv->plane, &tve200_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(drm, &priv->crtc,
+ &priv->plane, NULL,
+ &tve200_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(&priv->crtc, &tve200_crtc_helper_funcs);
+
+ ret = drm_encoder_init(drm, &priv->encoder,
+ &tve200_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
if (ret)
return ret;
+ priv->encoder.possible_crtcs = drm_crtc_mask(&priv->crtc);
+
return 0;
}
diff --git a/drivers/gpu/drm/tve200/tve200_drm.h b/drivers/gpu/drm/tve200/tve200_drm.h
index 5420b52ea16b..631a9f5d9aa6 100644
--- a/drivers/gpu/drm/tve200/tve200_drm.h
+++ b/drivers/gpu/drm/tve200/tve200_drm.h
@@ -15,8 +15,6 @@
#include <linux/irqreturn.h>
-#include <drm/drm_simple_kms_helper.h>
-
struct clk;
struct drm_bridge;
struct drm_connector;
@@ -107,7 +105,9 @@ struct tve200_drm_dev_private {
struct drm_connector *connector;
struct drm_panel *panel;
struct drm_bridge *bridge;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
void *regs;
struct clk *pclk;
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index 562f3f11812a..e2e8346acbb8 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -105,16 +105,21 @@ static int tve200_modeset_init(struct drm_device *dev)
goto out_bridge;
}
- ret = drm_simple_display_pipe_attach_bridge(&priv->pipe,
- bridge);
+ priv->panel = panel;
+ priv->connector = drm_panel_bridge_connector(bridge);
+ priv->bridge = bridge;
+
+ ret = drm_connector_attach_encoder(priv->connector, &priv->encoder);
if (ret) {
- dev_err(dev->dev, "failed to attach bridge\n");
+ dev_err(dev->dev, "failed to attach encoder\n");
goto out_bridge;
}
- priv->panel = panel;
- priv->connector = drm_panel_bridge_connector(bridge);
- priv->bridge = bridge;
+ ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0);
+ if (ret) {
+ dev_err(dev->dev, "failed to attach bridge\n");
+ goto out_bridge;
+ }
dev_info(dev->dev, "attached to panel %s\n",
dev_name(panel->dev));
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 9/9] drm/xen: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (7 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 8/9] drm/tve200: " Ze Huang
@ 2026-07-04 18:31 ` Ze Huang
2026-07-04 18:53 ` sashiko-bot
2026-07-06 7:27 ` [PATCH 0/9] drm: replace simple display pipe users with " Thomas Zimmermann
9 siblings, 1 reply; 23+ messages in thread
From: Ze Huang @ 2026-07-04 18:31 UTC (permalink / raw)
To: Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel, Ze Huang
Replace Xen frontend simple display pipe with explicit plane, CRTC and
encoder objects for each pipeline.
Keep generic plane-state validation before Xen-specific checks, and
install GEM framebuffer prepare helper explicitly.
Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
---
drivers/gpu/drm/xen/xen_drm_front.h | 6 +-
drivers/gpu/drm/xen/xen_drm_front_kms.c | 177 ++++++++++++++++++++++++--------
2 files changed, 138 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/xen/xen_drm_front.h b/drivers/gpu/drm/xen/xen_drm_front.h
index a987c78abe41..02138b5abef5 100644
--- a/drivers/gpu/drm/xen/xen_drm_front.h
+++ b/drivers/gpu/drm/xen/xen_drm_front.h
@@ -14,7 +14,7 @@
#include <linux/scatterlist.h>
#include <drm/drm_connector.h>
-#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_encoder.h>
#include "xen_drm_front_cfg.h"
@@ -100,7 +100,9 @@ struct xen_drm_front_drm_pipeline {
int index;
- struct drm_simple_display_pipe pipe;
+ struct drm_plane plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector conn;
/* These are only for connector mode checking */
diff --git a/drivers/gpu/drm/xen/xen_drm_front_kms.c b/drivers/gpu/drm/xen/xen_drm_front_kms.c
index 48772b5fe71c..57e1bef452f8 100644
--- a/drivers/gpu/drm/xen/xen_drm_front_kms.c
+++ b/drivers/gpu/drm/xen/xen_drm_front_kms.c
@@ -31,9 +31,9 @@
#define FRAME_DONE_TO_MS (XEN_DRM_FRONT_WAIT_BACK_MS + 100)
static struct xen_drm_front_drm_pipeline *
-to_xen_drm_pipeline(struct drm_simple_display_pipe *pipe)
+to_xen_drm_pipeline(struct drm_crtc *crtc)
{
- return container_of(pipe, struct xen_drm_front_drm_pipeline, pipe);
+ return container_of(crtc, struct xen_drm_front_drm_pipeline, crtc);
}
static void fb_destroy(struct drm_framebuffer *fb)
@@ -94,7 +94,7 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
static void send_pending_event(struct xen_drm_front_drm_pipeline *pipeline)
{
- struct drm_crtc *crtc = &pipeline->pipe.crtc;
+ struct drm_crtc *crtc = &pipeline->crtc;
struct drm_device *dev = crtc->dev;
unsigned long flags;
@@ -105,17 +105,15 @@ static void send_pending_event(struct xen_drm_front_drm_pipeline *pipeline)
spin_unlock_irqrestore(&dev->event_lock, flags);
}
-static void display_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void xen_drm_crtc_helper_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct xen_drm_front_drm_pipeline *pipeline =
- to_xen_drm_pipeline(pipe);
- struct drm_crtc *crtc = &pipe->crtc;
+ struct xen_drm_front_drm_pipeline *pipeline = to_xen_drm_pipeline(crtc);
+ struct drm_plane_state *plane_state = pipeline->plane.state;
struct drm_framebuffer *fb = plane_state->fb;
int ret, idx;
- if (!drm_dev_enter(pipe->crtc.dev, &idx))
+ if (!drm_dev_enter(crtc->dev, &idx))
return;
ret = xen_drm_front_mode_set(pipeline, crtc->x, crtc->y,
@@ -131,13 +129,13 @@ static void display_enable(struct drm_simple_display_pipe *pipe,
drm_dev_exit(idx);
}
-static void display_disable(struct drm_simple_display_pipe *pipe)
+static void xen_drm_crtc_helper_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
{
- struct xen_drm_front_drm_pipeline *pipeline =
- to_xen_drm_pipeline(pipe);
+ struct xen_drm_front_drm_pipeline *pipeline = to_xen_drm_pipeline(crtc);
int ret = 0, idx;
- if (drm_dev_enter(pipe->crtc.dev, &idx)) {
+ if (drm_dev_enter(crtc->dev, &idx)) {
ret = xen_drm_front_mode_set(pipeline, 0, 0, 0, 0, 0,
xen_drm_front_fb_to_cookie(NULL));
drm_dev_exit(idx);
@@ -177,12 +175,13 @@ static void pflip_to_worker(struct work_struct *work)
send_pending_event(pipeline);
}
-static bool display_send_page_flip(struct drm_simple_display_pipe *pipe,
+static bool display_send_page_flip(struct xen_drm_front_drm_pipeline *pipeline,
+ struct drm_atomic_commit *state,
struct drm_plane_state *old_plane_state)
{
struct drm_plane_state *plane_state =
- drm_atomic_get_new_plane_state(old_plane_state->state,
- &pipe->plane);
+ drm_atomic_get_new_plane_state(state,
+ &pipeline->plane);
/*
* If old_plane_state->fb is NULL and plane_state->fb is not,
@@ -193,8 +192,6 @@ static bool display_send_page_flip(struct drm_simple_display_pipe *pipe,
* sent to the backend as a part of display_set_config call.
*/
if (old_plane_state->fb && plane_state->fb) {
- struct xen_drm_front_drm_pipeline *pipeline =
- to_xen_drm_pipeline(pipe);
struct xen_drm_front_drm_info *drm_info = pipeline->drm_info;
int ret;
@@ -224,10 +221,30 @@ static bool display_send_page_flip(struct drm_simple_display_pipe *pipe,
return false;
}
-static int display_check(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *plane_state,
- struct drm_crtc_state *crtc_state)
+static int xen_drm_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+
+ if (!plane_state->visible)
+ return 0;
+
+ if (!crtc_state)
+ return 0;
+
/*
* Xen doesn't initialize vblanking via drm_vblank_init(), so
* DRM helpers assume that it doesn't handle vblanking and start
@@ -242,15 +259,19 @@ static int display_check(struct drm_simple_display_pipe *pipe,
return 0;
}
-static void display_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_plane_state)
+static void xen_drm_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
{
- struct xen_drm_front_drm_pipeline *pipeline =
- to_xen_drm_pipeline(pipe);
- struct drm_crtc *crtc = &pipe->crtc;
+ struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+ struct drm_crtc *crtc = plane->state->crtc ?: old_plane_state->crtc;
+ struct xen_drm_front_drm_pipeline *pipeline;
struct drm_pending_vblank_event *event;
int idx;
+ if (!crtc)
+ return;
+
+ pipeline = to_xen_drm_pipeline(crtc);
event = crtc->state->event;
if (event) {
struct drm_device *dev = crtc->dev;
@@ -265,7 +286,7 @@ static void display_update(struct drm_simple_display_pipe *pipe,
spin_unlock_irqrestore(&dev->event_lock, flags);
}
- if (!drm_dev_enter(pipe->crtc.dev, &idx)) {
+ if (!drm_dev_enter(crtc->dev, &idx)) {
send_pending_event(pipeline);
return;
}
@@ -278,19 +299,19 @@ static void display_update(struct drm_simple_display_pipe *pipe,
* If this is not a page flip, e.g. no flip done event from the backend
* is expected, then send now.
*/
- if (!display_send_page_flip(pipe, old_plane_state))
+ if (!display_send_page_flip(pipeline, state, old_plane_state))
send_pending_event(pipeline);
drm_dev_exit(idx);
}
static enum drm_mode_status
-display_mode_valid(struct drm_simple_display_pipe *pipe,
- const struct drm_display_mode *mode)
+xen_drm_crtc_helper_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
{
struct xen_drm_front_drm_pipeline *pipeline =
- container_of(pipe, struct xen_drm_front_drm_pipeline,
- pipe);
+ container_of(crtc, struct xen_drm_front_drm_pipeline,
+ crtc);
if (mode->hdisplay != pipeline->width)
return MODE_ERROR;
@@ -301,12 +322,55 @@ display_mode_valid(struct drm_simple_display_pipe *pipe,
return MODE_OK;
}
-static const struct drm_simple_display_pipe_funcs display_funcs = {
- .mode_valid = display_mode_valid,
- .enable = display_enable,
- .disable = display_disable,
- .check = display_check,
- .update = display_update,
+static int xen_drm_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ int ret;
+
+ if (!crtc_state->enable)
+ goto out;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+out:
+ return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static const struct drm_plane_helper_funcs display_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .atomic_check = xen_drm_plane_helper_atomic_check,
+ .atomic_update = xen_drm_plane_helper_atomic_update,
+};
+
+static const struct drm_plane_funcs display_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 const struct drm_crtc_helper_funcs display_crtc_helper_funcs = {
+ .mode_valid = xen_drm_crtc_helper_mode_valid,
+ .atomic_check = xen_drm_crtc_helper_atomic_check,
+ .atomic_enable = xen_drm_crtc_helper_atomic_enable,
+ .atomic_disable = xen_drm_crtc_helper_atomic_disable,
+};
+
+static const struct drm_crtc_funcs display_crtc_funcs = {
+ .reset = drm_atomic_helper_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_encoder_funcs display_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
static int display_pipe_init(struct xen_drm_front_drm_info *drm_info,
@@ -331,10 +395,37 @@ static int display_pipe_init(struct xen_drm_front_drm_info *drm_info,
formats = xen_drm_front_conn_get_formats(&format_count);
- return drm_simple_display_pipe_init(dev, &pipeline->pipe,
- &display_funcs, formats,
- format_count, NULL,
- &pipeline->conn);
+ ret = drm_universal_plane_init(dev, &pipeline->plane, 1,
+ &display_plane_funcs,
+ formats, format_count,
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret)
+ return ret;
+
+ drm_plane_helper_add(&pipeline->plane, &display_plane_helper_funcs);
+
+ ret = drm_crtc_init_with_planes(dev, &pipeline->crtc,
+ &pipeline->plane, NULL,
+ &display_crtc_funcs, NULL);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(&pipeline->crtc, &display_crtc_helper_funcs);
+
+ ret = drm_encoder_init(dev, &pipeline->encoder,
+ &display_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret)
+ return ret;
+
+ pipeline->encoder.possible_crtcs = drm_crtc_mask(&pipeline->crtc);
+
+ ret = drm_connector_attach_encoder(&pipeline->conn, &pipeline->encoder);
+ if (ret)
+ return ret;
+
+ return 0;
}
int xen_drm_front_kms_init(struct xen_drm_front_drm_info *drm_info)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
@ 2026-07-04 18:43 ` sashiko-bot
2026-07-06 7:43 ` Thomas Zimmermann
2026-07-06 8:01 ` Thomas Zimmermann
1 sibling, 1 reply; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:43 UTC (permalink / raw)
To: Ze Huang; +Cc: Frank.Li, dri-devel, imx
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] NULL pointer dereference when enabling CRTC without a primary plane framebuffer.
Pre-existing issues:
- [High] Unsafe direct access of global plane and CRTC state pointers in atomic commit callbacks.
--
commit 6344532446cab68117e44660f110ef0c8aec15d7
Author: Ze Huang <ze.huang@oss.qualcomm.com>
drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
This commit replaces drm_simple_display_pipe with regular atomic helpers.
It instantiates the plane, CRTC, and encoder directly, wiring them up with
standard atomic helpers to remove the dependency on the simple-KMS interface.
> diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c
> index c93d61ac0bb75..375cdb79e4e89 100644
> --- a/drivers/gpu/drm/tiny/arcpgu.c
> +++ b/drivers/gpu/drm/tiny/arcpgu.c
[ ... ]
> @@ -117,7 +117,7 @@ static const u32 arc_pgu_supported_formats[] = {
>
> static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
> {
> - const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb;
> + const struct drm_framebuffer *fb = arcpgu->plane.state->fb;
[Severity: High]
This isn't a bug introduced by this patch, but does reading the global
plane->state pointer here instead of using the state passed via the
drm_atomic_commit parameter cause issues during non-blocking atomic commits?
If multiple non-blocking commits are queued rapidly, the global pointers
advance to the newest state. When the first commit's commit_tail finally
executes, reading plane->state directly instead of extracting the specific
commit's state could cause the older commit to program the hardware using a
future commit's state.
[Severity: High]
Can this dereference a NULL pointer if the CRTC is enabled without a primary
plane framebuffer?
The transition from drm_simple_display_pipe to generic atomic helpers removed
the underlying protection that previously prevented the CRTC from enabling
without a valid framebuffer. If userspace performs an atomic commit that
enables the CRTC but sets the primary plane's framebuffer to 0 (NULL),
drm_atomic_helper_check_crtc_primary_plane verifies the plane is attached but
does not enforce plane_state->fb != NULL.
This would allow the commit to proceed to arcpgu_crtc_helper_atomic_enable(),
which calls arc_pgu_mode_set(), and then arc_pgu_set_pxl_fmt(), blindly
dereferencing fb->format->format here.
> uint32_t pixel_format = fb->format->format;
> u32 format = DRM_FORMAT_INVALID;
> int i;
[ ... ]
> @@ -155,7 +155,7 @@ static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *p
>
> static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
> {
> - struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode;
> + struct drm_display_mode *m = &arcpgu->crtc.state->adjusted_mode;
[Severity: High]
This is a pre-existing issue, but could reading the global crtc->state pointer
directly cause hardware state corruption or frame tearing?
Similar to the plane state issue above, if rapid non-blocking atomic commits
are generated, arc_pgu_mode_set() might read from the newest global state
rather than the specific commit's state object.
> u32 val;
>
> arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
[ ... ]
> @@ -218,35 +218,106 @@ static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
> ~ARCPGU_CTRL_ENABLE_MASK);
> }
>
> -static void arc_pgu_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *state)
> +static void arcpgu_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> struct arcpgu_drm_private *arcpgu;
> struct drm_gem_dma_object *gem;
>
> - if (!pipe->plane.state->fb)
> + if (!plane->state->fb)
[Severity: High]
This is a pre-existing issue, but does arcpgu_plane_helper_atomic_update()
safely handle non-blocking atomic commits when reading plane->state->fb
directly?
Reading the global plane->state pointer instead of extracting the new plane
state from the drm_atomic_commit parameter could result in the same race
condition during asynchronous commit_tail execution.
> return;
>
> - arcpgu = pipe_to_arcpgu_priv(pipe);
> - gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
> + arcpgu = dev_to_arcpgu(plane->dev);
> + gem = drm_fb_dma_get_gem_obj(plane->state->fb, 0);
> arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->dma_addr);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 4/9] drm/mcde: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 4/9] drm/mcde: " Ze Huang
@ 2026-07-04 18:44 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:44 UTC (permalink / raw)
To: Ze Huang; +Cc: dri-devel, Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] Uninitialized NULL pointer dereference when attaching encoder to connector.
Pre-existing issues:
- [High] Atomic callbacks directly access global `obj->state` pointers instead of using the state passed in the `drm_atomic_commit` parameter.
--
commit 098f44a54758ae5b340970e3384cc9330909fb06
Author: Ze Huang <ze.huang@oss.qualcomm.com>
drm/mcde: replace struct drm_simple_display_pipe with regular atomic helpers
Convert MCDE to explicit plane, CRTC and encoder objects.
Keep FIFO, event and framebuffer update sequencing intact, and install
GEM framebuffer prepare callback explicitly.
> diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
> index 257a6e84dd583..4d86fa5030ebf 100644
> --- a/drivers/gpu/drm/mcde/mcde_display.c
> +++ b/drivers/gpu/drm/mcde/mcde_display.c
[ ... ]
> @@ -1149,16 +1171,14 @@ static void mcde_setup_dsi(struct mcde *mcde, const struct drm_display_mode *mod
> *dsi_formatter_frame = formatter_frame;
> }
>
> -static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
> - struct drm_crtc_state *cstate,
> - struct drm_plane_state *plane_state)
> +static void mcde_crtc_helper_atomic_enable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_plane *plane = &pipe->plane;
> struct drm_device *drm = crtc->dev;
> struct mcde *mcde = to_mcde(drm);
> + struct drm_crtc_state *cstate = crtc->state;
[Severity: High]
This isn't a bug introduced by this patch, as it was inherited from the
simple-kms implementation, but does this newly added line introduce a data
race?
In non-blocking commits, the DRM core asynchronously executes the worker
thread while simultaneously swapping the global state pointers. Reading the
global crtc->state here instead of fetching the new state from the state
parameter (e.g. using drm_atomic_get_new_crtc_state()) can race with
subsequent commits, potentially programming the hardware with mismatched
states.
[ ... ]
> -static void mcde_display_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *old_pstate)
> +static void mcde_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_device *drm = crtc->dev;
> - struct mcde *mcde = to_mcde(drm);
> - struct drm_pending_vblank_event *event = crtc->state->event;
> - struct drm_plane *plane = &pipe->plane;
> + struct drm_crtc *crtc = plane->state->crtc;
> + struct drm_device *drm;
> + struct mcde *mcde;
> + struct drm_pending_vblank_event *event;
> struct drm_plane_state *pstate = plane->state;
> struct drm_framebuffer *fb = pstate->fb;
>
> + if (!crtc)
> + return;
> +
> + drm = crtc->dev;
> + mcde = to_mcde(drm);
> + event = crtc->state->event;
[Severity: High]
This is a pre-existing issue, but does accessing crtc->state->event here
continue to expose a time-of-check to time-of-use race?
If a compositor queues multiple rapid non-blocking commits, reading the event
from the global state might clear the event belonging to a subsequent commit,
leaving the later commit's worker with a NULL event and hanging the compositor
waiting for a page flip.
[ ... ]
> @@ -1510,11 +1577,30 @@ int mcde_display_init(struct drm_device *drm)
> if (ret)
> return ret;
>
> - ret = drm_simple_display_pipe_init(drm, &mcde->pipe,
> - &mcde_display_funcs,
> - formats, ARRAY_SIZE(formats),
> - NULL,
> - mcde->connector);
> + ret = drm_universal_plane_init(drm, &mcde->plane, 0,
> + &mcde_plane_funcs,
> + formats, ARRAY_SIZE(formats),
> + NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
> + if (ret)
> + return ret;
> +
> + drm_plane_helper_add(&mcde->plane, &mcde_plane_helper_funcs);
> +
> + ret = drm_crtc_init_with_planes(drm, &mcde->crtc, &mcde->plane,
> + NULL, &mcde_crtc_funcs, NULL);
> + if (ret)
> + return ret;
> +
> + drm_crtc_helper_add(&mcde->crtc, &mcde_crtc_helper_funcs);
> +
> + ret = drm_encoder_init(drm, &mcde->encoder, &mcde_encoder_funcs,
> + DRM_MODE_ENCODER_NONE, NULL);
> + if (ret)
> + return ret;
> +
> + mcde->encoder.possible_crtcs = drm_crtc_mask(&mcde->crtc);
> +
> + ret = drm_connector_attach_encoder(mcde->connector, &mcde->encoder);
[Severity: Critical]
Will this lead to a NULL pointer dereference during probe?
mcde->connector appears to be a zero-initialized struct member that is not
allocated or populated prior to this call. Because
drm_connector_attach_encoder() unconditionally dereferences the connector
argument, passing it a NULL pointer will lead to an immediate kernel oops.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 5/9] drm/pl111: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 5/9] drm/pl111: " Ze Huang
@ 2026-07-04 18:45 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:45 UTC (permalink / raw)
To: Ze Huang; +Cc: dri-devel, Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Unconsumed CRTC VBLANK event when disabling the CRTC causes a memory leak and userspace hang.
--
commit f35a0721a36bbf78ed41d1be8bb186efb88c9092
Author: Ze Huang <ze.huang@oss.qualcomm.com>
Subject: drm/pl111: replace struct drm_simple_display_pipe with regular atomic helpers
This commit replaces the PL111 simple display pipe with explicit plane, CRTC,
and encoder objects. It preserves existing hardware programming and vblank
behavior while using the GEM framebuffer prepare helper.
> diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
> index 5d10bc5fdf1f6..b1bdd4c9dbe68 100644
> --- a/drivers/gpu/drm/pl111/pl111_display.c
> +++ b/drivers/gpu/drm/pl111/pl111_display.c
[ ... ]
> -static void pl111_display_disable(struct drm_simple_display_pipe *pipe)
> +static void pl111_crtc_helper_atomic_disable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> struct drm_device *drm = crtc->dev;
> struct pl111_drm_dev_private *priv = drm->dev_private;
> u32 cntl;
[ ... ]
> -static void pl111_display_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *old_pstate)
> +static void pl111_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_device *drm = crtc->dev;
> - struct pl111_drm_dev_private *priv = drm->dev_private;
> - struct drm_pending_vblank_event *event = crtc->state->event;
> - struct drm_plane *plane = &pipe->plane;
> + struct drm_crtc *crtc = plane->state->crtc;
> + struct drm_device *drm;
> + struct pl111_drm_dev_private *priv;
> + struct drm_pending_vblank_event *event;
> struct drm_plane_state *pstate = plane->state;
> struct drm_framebuffer *fb = pstate->fb;
>
> + if (!crtc)
> + return;
> +
[Severity: High]
When disabling the CRTC, does this code leak the vblank event?
If an atomic commit disables the CRTC and requests a completion event, this
early return in pl111_plane_helper_atomic_update() triggers because
plane->state->crtc is NULL when the plane is disabled.
Since the event consumption logic previously provided by the
drm_simple_display_pipe framework has been removed, and
pl111_crtc_helper_atomic_disable() also lacks logic to consume
crtc->state->event, won't the event remain attached and unconsumed?
This can trigger a kernel warning in drm_atomic_helper_commit_hw_done()
(which warns if the event is still attached) and cause userspace
compositors to hang indefinitely waiting for the page flip event.
> + drm = crtc->dev;
> + priv = drm->dev_private;
> + event = crtc->state->event;
> +
> if (fb) {
> u32 addr = drm_fb_dma_get_gem_addr(fb, pstate, 0);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 6/9] drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 6/9] drm/gm12u320: " Ze Huang
@ 2026-07-04 18:45 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:45 UTC (permalink / raw)
To: Ze Huang; +Cc: Frank.Li, imx, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Race condition and potential NULL pointer dereference due to direct access to `plane->state` and missing visibility checks in atomic callbacks.
Pre-existing issues:
- [High] Sleeping in atomic context within CRTC and plane atomic callbacks.
--
commit a10b3d5781e3b7143f432c86643ef47e8ddc3a9a
Author: Ze Huang <ze.huang@oss.qualcomm.com>
drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
Convert gm12u320 to direct primary plane, CRTC and encoder setup.
Keep shadow-plane helper state, framebuffer access helpers and
no-scaling plane-state check from simple-KMS path.
> diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
> index 4ad074337af07..b476793d0401e 100644
> --- a/drivers/gpu/drm/tiny/gm12u320.c
> +++ b/drivers/gpu/drm/tiny/gm12u320.c
[ ... ]
> +static void gm12u320_crtc_helper_atomic_enable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *commit)
> {
> struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
> - struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
> + struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
> + struct drm_plane_state *plane_state = crtc->primary->state;
[Severity: High]
Will this cause a NULL pointer dereference if a subsequent commit disables
the plane?
Because non-blocking commits execute asynchronously, crtc->primary->state
reflects the state of the most recently queued commit, rather than the
commit currently executing. If a fast subsequent commit disables the plane,
plane_state->fb could be NULL.
Should this use drm_atomic_get_new_plane_state(commit, crtc->primary) to
get the state tied to this specific commit?
> struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
>
> gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
> gm12u320_fb_mark_dirty(plane_state->fb, &shadow_plane_state->data[0], &rect);
> }
>
> -static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
> +static void gm12u320_crtc_helper_atomic_disable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *commit)
> {
> - struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
> + struct gm12u320_device *gm12u320 = to_gm12u320(crtc->dev);
>
> gm12u320_stop_fb_update(gm12u320);
[Severity: High]
This isn't a bug introduced by this patch, but does gm12u320_stop_fb_update()
sleep in atomic context?
gm12u320_stop_fb_update() calls cancel_delayed_work_sync() and mutex_lock().
Since CRTC atomic disable callbacks can run in atomic context during
non-blocking commits, sleeping here is forbidden and could cause kernel
warnings or deadlocks.
> }
>
> -static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *old_state)
> +static void gm12u320_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *commit)
> {
> - struct drm_plane_state *state = pipe->plane.state;
> + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(commit, plane);
> + struct drm_plane_state *state = plane->state;
[Severity: High]
Could accessing plane->state directly here lead to a crash?
Similar to the CRTC enable function, reading plane->state directly instead of
using drm_atomic_get_new_plane_state(commit, plane) accesses the future
state.
If a subsequent commit disables the plane, state->fb will be NULL. In this
scenario, drm_atomic_helper_damage_merged() returns true for plane
disablements, leading to gm12u320_fb_mark_dirty() being called with a NULL
fb, which will oops when trying to dereference fb->dev.
This wasn't possible before because the drm_simple_kms_helper explicitly
checked if (!plane->state->visible) before calling the update hook, but
that protection is dropped in this conversion.
> + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
> struct drm_rect rect;
>
> - if (drm_atomic_helper_damage_merged(old_state, state, &rect))
> + if (drm_atomic_helper_damage_merged(old_state, state, &rect))
> gm12u320_fb_mark_dirty(state->fb, &shadow_plane_state->data[0], &rect);
[Severity: High]
This is a pre-existing issue, but does gm12u320_fb_mark_dirty() sleep in
atomic context?
It calls mutex_lock(&gm12u320->fb_update.lock), which is unsafe to call
from a plane update callback during non-blocking commits, as these run
in atomic context.
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 8/9] drm/tve200: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 8/9] drm/tve200: " Ze Huang
@ 2026-07-04 18:46 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:46 UTC (permalink / raw)
To: Ze Huang; +Cc: dri-devel, Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Vblank event is leaked on CRTC disable and may be delivered prematurely on CRTC enable.
- [Medium] Calling `drm_connector_attach_encoder()` on an uninitialized connector.
Pre-existing issues:
- [High] Calling `usleep_range()` from a CRTC `atomic_enable` callback.
- [High] Direct, lockless access to global state pointers `plane->state` and `crtc->state` in atomic commit callbacks.
--
Note: The format of this review has been altered to a free-form summary due
to mailing list quoting restrictions.
Commit: 2c5e6508a64461d7a207075c4328681142076aef
Author: Ze Huang <ze.huang@oss.qualcomm.com>
Subject: drm/tve200: replace struct drm_simple_display_pipe with regular atomic helpers
Summary: This commit converts the TVE200 driver to use explicit plane, CRTC,
and encoder objects instead of the simple display pipe helpers, while keeping
generic plane-state validation and GEM framebuffer preparation.
[Severity: High]
In drivers/gpu/drm/tve200/tve200_display.c,
tve200_plane_helper_atomic_update() checks if the CRTC is NULL and returns
early. Does this cause the pending vblank event to be leaked during a CRTC
disable, since the plane is also disabled (making crtc NULL) and the event
is never consumed?
Additionally, since commit_planes() executes before commit_modeset_enables(),
is the event processed and delivered prematurely on CRTC enable? Could the
event consumption be moved to CRTC callbacks like atomic_enable,
atomic_disable, or atomic_flush instead of handling it in the plane callback?
[Severity: Medium]
In drivers/gpu/drm/tve200/tve200_drv.c, tve200_modeset_init() gets the
connector using drm_panel_bridge_connector() and immediately passes it to
drm_connector_attach_encoder(). Since drm_bridge_attach() has not run yet,
is this modifying an uninitialized DRM object structure?
Would it be better to rely on panel_bridge_attach(), which already
automatically handles attaching the connector to the encoder, making this
manual call redundant?
[Severity: High]
This isn't a bug introduced by this patch, but in
drivers/gpu/drm/tve200/tve200_display.c, tve200_crtc_helper_atomic_enable()
contains a hardware polling loop that calls usleep_range(30000, 50000).
Since CRTC atomic enable callbacks run in atomic context during non-blocking
commits where sleeping is strictly forbidden, can this cause kernel warnings
or deadlocks? Should this be replaced with non-sleeping delay functions like
udelay or ndelay?
[Severity: High]
This is a pre-existing issue, but it appears to be expanded here. In
drivers/gpu/drm/tve200/tve200_display.c, tve200_plane_helper_atomic_update()
and tve200_crtc_helper_atomic_enable() directly access the global state
pointers plane->state and priv->plane.state.
In non-blocking commits, these global pointers can be swapped if userspace
submits a concurrent commit. Does this create a TOCTOU race where a
concurrent commit setting the framebuffer to NULL could lead to a NULL
pointer dereference when accessing fb->format->format? Should this code use
the proper drm_atomic_get_new_plane_state() (or equivalent) accessors on the
passed state object instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=8
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 3/9] drm/imx: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 3/9] drm/imx: " Ze Huang
@ 2026-07-04 18:46 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:46 UTC (permalink / raw)
To: Ze Huang; +Cc: dri-devel, imx, Frank.Li
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] CRTC disable operations fail unconditionally due to early mode validation.
- [High] Plane atomic check rejects offline plane updates, breaking display disabling.
- [High] Missing drm_crtc_vblank_on() and drm_crtc_vblank_off() calls break vblank synchronization.
- [High] Missing .enable_vblank hook in drm_crtc_funcs causes kernel warnings and breaks vblank.
Pre-existing issues:
- [High] Racy direct state access in atomic callbacks leading to potential use-after-free.
- [High] Unbounded clock reference count leak on CRTC disable cycles.
--
commit 404ed5ff8b68b90a5ba44e0c6a32b76744948691
Author: Ze Huang <ze.huang@oss.qualcomm.com>
drm/imx: replace struct drm_simple_display_pipe with regular atomic helpers
This commit converts the i.MX LCDC driver to use explicit primary plane,
CRTC, and encoder objects instead of the simple display pipe helpers. It
retains the no-scaling plane checks and GEM framebuffer prepare callbacks,
adjusting hardware state only when a framebuffer is present.
> diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
> index c67fe80b8115e..f0c4c19f91166 100644
> --- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
> +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
[ ... ]
> -static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
> +static void imx_lcdc_update_hw_registers(struct drm_crtc *crtc,
> struct drm_plane_state *old_state,
> bool mode_set)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_plane_state *new_state = pipe->plane.state;
> + struct drm_plane_state *new_state = crtc->primary->state;
[Severity: High]
This is a pre-existing issue, but does this direct state access create a race
condition?
In non-blocking atomic commits, the DRM core synchronously swaps these object
pointers before the asynchronous worker executes. If multiple commits are
queued, we might retrieve the state for a future commit instead of the
current one, which could lead to programming hardware with an unpinned or
incorrect framebuffer.
[ ... ]
> @@ -230,34 +231,34 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
[ ... ]
> imx_lcdc_update_hw_registers(crtc, NULL, true);
>
> /* Enable VBLANK Interrupt */
> writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER);
> }
[Severity: High]
Should drm_crtc_vblank_on() be called here?
Without notifying the DRM core that the hardware vblank interrupt is enabled,
the core might consider the vblank state as off. This causes vblank waits to
fail unconditionally, forcing the plane atomic update to immediately send page
flip events to userspace rather than waiting for vsync.
[ ... ]
> -static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
> +static void imx_lcdc_crtc_helper_atomic_disable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
> - struct drm_crtc *crtc = &lcdc->pipe.crtc;
> + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev);
> struct drm_pending_vblank_event *event;
>
> clk_disable_unprepare(lcdc->clk_ahb);
> clk_disable_unprepare(lcdc->clk_ipg);
>
> - if (pipe->crtc.enabled)
> + if (crtc->enabled)
> clk_disable_unprepare(lcdc->clk_per);
[Severity: High]
This is a pre-existing issue, but does this leak a clock reference?
The atomic_disable callback runs after the DRM core updates legacy state
variables, so crtc->enabled is guaranteed to be false here. This means the
clock might never be disabled during a DPMS cycle.
[ ... ]
> spin_unlock_irq(&lcdc->drm.event_lock);
>
> /* Disable VBLANK Interrupt */
> writel(0, lcdc->base + IMX21LCDC_LIER);
> }
[Severity: High]
Is a call to drm_crtc_vblank_off() needed here to synchronously disable vblank
processing in the DRM core when shutting down the hardware interrupt?
[ ... ]
> -static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *plane_state,
> - struct drm_crtc_state *crtc_state)
> +static int imx_lcdc_crtc_helper_atomic_check(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> const struct drm_display_mode *mode = &crtc_state->mode;
> - const struct drm_display_mode *old_mode = &pipe->crtc.state->mode;
> + const struct drm_display_mode *old_mode = &crtc->state->mode;
> + int ret;
>
> if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
> mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
> mode->hdisplay % 0x10) { /* must be multiple of 16 */
[Severity: High]
Will this cause CRTC disable operations to fail?
When userspace disables the CRTC (like via DPMS OFF), crtc_state->enable is
false and the display mode might be zeroed out. Validating the mode dimensions
before checking crtc_state->enable could cause this check to return -EINVAL
unconditionally.
[ ... ]
> -static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *old_state)
> +static void imx_lcdc_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_pending_vblank_event *event = crtc->state->event;
> - struct drm_plane_state *new_state = pipe->plane.state;
> + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
> + struct drm_plane_state *new_state = plane->state;
[Severity: High]
This is a pre-existing issue, but does assigning new_state directly from
plane->state risk a race condition?
For non-blocking commits, fetching the new plane state from the
drm_atomic_commit object avoids reading state from a potentially concurrent
future commit.
[ ... ]
> +static int imx_lcdc_plane_helper_atomic_check(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> +{
> + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
> + struct drm_crtc *crtc = plane_state->crtc;
> + struct drm_crtc_state *crtc_state = NULL;
> + int ret;
> +
> + if (crtc)
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
> + DRM_PLANE_NO_SCALING,
> + DRM_PLANE_NO_SCALING,
> + false, false);
[Severity: High]
Does passing false for the can_update_disabled parameter reject offline plane
updates?
If userspace tries to disable the CRTC without detaching the framebuffer, the
helper will see a plane on a disabled CRTC and return -EINVAL, which breaks
display disabling.
[ ... ]
> +static const struct drm_crtc_funcs imx_lcdc_crtc_funcs = {
> + .reset = drm_atomic_helper_crtc_reset,
> + .destroy = drm_crtc_cleanup,
> + .set_config = drm_atomic_helper_set_config,
> + .page_flip = drm_atomic_helper_page_flip,
> + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
[Severity: High]
Should this struct include implementations for .enable_vblank and
.disable_vblank?
When drm_vblank_get() is called to wait for vsync, the DRM core requires the
enable_vblank hook. Omitting these causes a kernel warning and makes the wait
return -EINVAL.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/9] drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 2/9] drm/aspeed: " Ze Huang
@ 2026-07-04 18:52 ` sashiko-bot
2026-07-06 8:31 ` Thomas Zimmermann
1 sibling, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:52 UTC (permalink / raw)
To: Ze Huang; +Cc: dri-devel, Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Lockless access to global atomic state pointers in commit path causes TOCTOU races and potential NULL pointer dereferences.
- [High] CRTC vblank events are prematurely consumed in the plane's atomic_update callback.
--
commit 7172c80142608f5bf7d738f64624b3135c44fdba
Author: Ze Huang <ze.huang@oss.qualcomm.com>
Subject: drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
This patch replaces the simple display pipe in the aspeed DRM driver with
explicit plane, CRTC, and encoder objects. It removes the intermediate layer
and wires up the standard atomic helpers for modesetting and page flips.
> diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
> index 7877a57b8e265..3294795c31c4c 100644
> --- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
> +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
[ ... ]
> static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
> {
> - struct drm_crtc *crtc = &priv->pipe.crtc;
> + struct drm_crtc *crtc = &priv->crtc;
> struct drm_device *drm = crtc->dev;
> const u32 format = crtc->primary->state->fb->format->format;
> u32 ctrl1;
[Severity: High]
This is a pre-existing issue, but does this lockless access to the global
state pointers in aspeed_gfx_set_pixel_fmt risk a NULL pointer dereference or
TOCTOU race?
If a concurrent non-blocking atomic commit disables the primary plane while
this runs in a worker thread, the global state could be swapped, potentially
making the framebuffer pointer NULL.
Would it be safer to extract the commit-specific state using the atomic
helpers instead of reading directly from crtc->primary->state->fb?
[ ... ]
> static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
> {
> - struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
> + struct drm_display_mode *m = &priv->crtc.state->adjusted_mode;
> u32 ctrl1, d_offset, t_count, bpp;
> int err;
[Severity: High]
This is also a pre-existing issue carried over during the conversion, but can
reading directly from the global crtc.state cause a similar TOCTOU race?
Reading the adjusted_mode directly from the global state pointer bypasses the
atomic commit's local state. Could this function be updated to use the local
state passed to the atomic hooks?
[ ... ]
> -static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *plane_state)
> +static void aspeed_gfx_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_framebuffer *fb = pipe->plane.state->fb;
> + struct aspeed_gfx *priv = container_of(plane, struct aspeed_gfx, plane);
> + struct drm_crtc *crtc = &priv->crtc;
> + struct drm_framebuffer *fb = plane->state->fb;
> struct drm_pending_vblank_event *event;
> struct drm_gem_dma_object *gem;
[Severity: High]
This is a pre-existing issue, but does consuming the vblank event directly in
the plane update phase risk premature signaling?
Looking at aspeed_gfx_plane_helper_atomic_update, the driver steals the event
from the global state (crtc->state->event) before the CRTC is fully enabled
and programmed.
Could userspace prematurely free or overwrite the framebuffer while the
hardware is still scanning it out due to this early signaling?
Would it be more appropriate to handle the event in an atomic_flush hook
where standard atomic flush mechanisms can ensure hardware changes are fully
complete?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 9/9] drm/xen: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 9/9] drm/xen: " Ze Huang
@ 2026-07-04 18:53 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-07-04 18:53 UTC (permalink / raw)
To: Ze Huang; +Cc: imx, dri-devel, Frank.Li
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Hardcoded `possible_crtcs` mask restricts all primary planes to CRTC 0, breaking multi-display support.
- [High] Direct access of global `plane->state` and `crtc->state` in atomic commit hooks leads to race conditions and stalled events.
--
commit 8a32b307abb3d8e006d032d01a1802aa1e4ae6f0
Author: Ze Huang <ze.huang@oss.qualcomm.com>
drm/xen: replace struct drm_simple_display_pipe with regular atomic helpers
Replace Xen frontend simple display pipe with explicit plane, CRTC and
encoder objects for each pipeline. Keep generic plane-state validation
before Xen-specific checks, and install GEM framebuffer prepare helper
explicitly.
> diff --git a/drivers/gpu/drm/xen/xen_drm_front_kms.c b/drivers/gpu/drm/xen/xen_drm_front_kms.c
> index 48772b5fe71cf..57e1bef452f83 100644
> --- a/drivers/gpu/drm/xen/xen_drm_front_kms.c
> +++ b/drivers/gpu/drm/xen/xen_drm_front_kms.c
[ ... ]
> @@ -105,17 +105,15 @@ static void send_pending_event(struct xen_drm_front_drm_pipeline *pipeline)
> spin_unlock_irqrestore(&dev->event_lock, flags);
> }
>
> -static void display_enable(struct drm_simple_display_pipe *pipe,
> - struct drm_crtc_state *crtc_state,
> - struct drm_plane_state *plane_state)
> +static void xen_drm_crtc_helper_atomic_enable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct xen_drm_front_drm_pipeline *pipeline =
> - to_xen_drm_pipeline(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> + struct xen_drm_front_drm_pipeline *pipeline = to_xen_drm_pipeline(crtc);
> + struct drm_plane_state *plane_state = pipeline->plane.state;
[Severity: High]
Is it safe to access the global pipeline->plane.state directly here? If
non-blocking commits overlap, this pointer might point to a newer,
uncommitted state. Should this code use drm_atomic_get_new_plane_state()
to extract the state from the drm_atomic_commit object instead?
> struct drm_framebuffer *fb = plane_state->fb;
> int ret, idx;
[ ... ]
> @@ -242,15 +259,19 @@ static int display_check(struct drm_simple_display_pipe *pipe,
> return 0;
> }
>
> -static void display_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *old_plane_state)
> +static void xen_drm_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct xen_drm_front_drm_pipeline *pipeline =
> - to_xen_drm_pipeline(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
> + struct drm_crtc *crtc = plane->state->crtc ?: old_plane_state->crtc;
[Severity: High]
Can accessing the global plane->state pointer here cause race conditions?
It might be safer to use drm_atomic_get_new_plane_state(state, plane)->crtc
to ensure the correct state is referenced during the atomic commit.
> + struct xen_drm_front_drm_pipeline *pipeline;
> struct drm_pending_vblank_event *event;
> int idx;
>
> + if (!crtc)
> + return;
> +
> + pipeline = to_xen_drm_pipeline(crtc);
> event = crtc->state->event;
[Severity: High]
Similarly, does reading and modifying the global crtc->state directly risk
fetching state from a concurrent commit instead of the current one?
> if (event) {
> struct drm_device *dev = crtc->dev;
[ ... ]
> @@ -331,10 +395,37 @@ static int display_pipe_init(struct xen_drm_front_drm_info *drm_info,
>
> formats = xen_drm_front_conn_get_formats(&format_count);
>
> - return drm_simple_display_pipe_init(dev, &pipeline->pipe,
> - &display_funcs, formats,
> - format_count, NULL,
> - &pipeline->conn);
> + ret = drm_universal_plane_init(dev, &pipeline->plane, 1,
> + &display_plane_funcs,
> + formats, format_count,
> + NULL,
> + DRM_PLANE_TYPE_PRIMARY, NULL);
[Severity: High]
Does hardcoding the possible_crtcs mask to 1 restrict all primary planes to
CRTC 0? For pipelines with an index greater than 0, the CRTC might fail to
find a compatible primary plane during atomic validation. Would using
1 << pipeline->index allow multi-display configurations to work correctly?
> + if (ret)
> + return ret;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260705-drm-simple-kms-removal-v1-0-b4e1ca053623@oss.qualcomm.com?part=9
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/9] drm: replace simple display pipe users with atomic helpers
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
` (8 preceding siblings ...)
2026-07-04 18:31 ` [PATCH 9/9] drm/xen: " Ze Huang
@ 2026-07-06 7:27 ` Thomas Zimmermann
2026-07-06 8:22 ` Ze Huang
9 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-07-06 7:27 UTC (permalink / raw)
To: Ze Huang, Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
David Airlie, Simona Vetter, Joel Stanley, Andrew Jeffery,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel
Hi
Am 04.07.26 um 20:31 schrieb Ze Huang:
> struct drm_simple_display_pipe was meant to simplify simple DRM
> drivers, but instead adds an extra wrapper around normal DRM atomic
> helper setup. As noted in Documentation/gpu/todo.rst, remaining users
> should be converted to regular atomic helpers and stop depending on the
> simple-KMS interfaces.
>
> This series converts the following drivers:
>
> - arcpgu
> - aspeed
> - imx lcdc
> - mcde
> - pl111
> - gm12u320
> - repaper
> - tve200
> - xen frontend
>
> Each patch replaces drm_simple_display_pipe_init() with explicit
> primary plane, CRTC and encoder setup, and moves the old simple-pipe
> callbacks into regular plane and CRTC helper callbacks named according
> to local driver conventions.
>
> The conversions preserve helper behavior that used to be implicit in
> drm_simple_kms_helper.c, including plane-state validation, CRTC
> primary-plane checks, affected-plane propagation, framebuffer prepare
> handling, and existing event/vblank flow where applicable.
>
> Result is less helper indirection and more explicit driver-side atomic
> wiring, with no remaining simple-KMS dependency in these drivers.
>
> These changes are build-tested only. No hardware testing has been
> performed on the affected devices.
Thanks a lot for the series. That's quite a nice cleanup. Did you use
any AI to create these patches?
Best regards
Thomas
>
> This series is based on drm-next-2026-06-27.
>
> Thanks,
> Ze Huang
>
> Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
> ---
> Ze Huang (9):
> drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/imx: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/mcde: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/pl111: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/gm12u320: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/repaper: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/tve200: replace struct drm_simple_display_pipe with regular atomic helpers
> drm/xen: replace struct drm_simple_display_pipe with regular atomic helpers
>
> drivers/gpu/drm/aspeed/aspeed_gfx.h | 5 +-
> drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 156 ++++++++++++++++------
> drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 3 +-
> drivers/gpu/drm/imx/lcdc/imx-lcdc.c | 178 ++++++++++++++++++-------
> drivers/gpu/drm/mcde/mcde_display.c | 162 ++++++++++++++++------
> drivers/gpu/drm/mcde/mcde_drm.h | 6 +-
> drivers/gpu/drm/mcde/mcde_drv.c | 3 +-
> drivers/gpu/drm/pl111/pl111_display.c | 174 ++++++++++++++++++------
> drivers/gpu/drm/pl111/pl111_drm.h | 5 +-
> drivers/gpu/drm/pl111/pl111_drv.c | 3 +-
> drivers/gpu/drm/tiny/arcpgu.c | 165 ++++++++++++++++++-----
> drivers/gpu/drm/tiny/gm12u320.c | 128 ++++++++++++++----
> drivers/gpu/drm/tiny/repaper.c | 130 ++++++++++++++----
> drivers/gpu/drm/tve200/tve200_display.c | 221 +++++++++++++++++++++----------
> drivers/gpu/drm/tve200/tve200_drm.h | 6 +-
> drivers/gpu/drm/tve200/tve200_drv.c | 17 ++-
> drivers/gpu/drm/xen/xen_drm_front.h | 6 +-
> drivers/gpu/drm/xen/xen_drm_front_kms.c | 177 +++++++++++++++++++------
> 18 files changed, 1164 insertions(+), 381 deletions(-)
> ---
> base-commit: 3696d07837d1df13a5603d77f667685e7dfb3c53
> change-id: 20260704-drm-simple-kms-removal-01a031c6a129
>
> Best regards,
> --
> Ze Huang <ze.huang@oss.qualcomm.com>
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:43 ` sashiko-bot
@ 2026-07-06 7:43 ` Thomas Zimmermann
0 siblings, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-07-06 7:43 UTC (permalink / raw)
To: sashiko-reviews, Ze Huang; +Cc: Frank.Li, dri-devel, imx
Am 04.07.26 um 20:43 schrieb sashiko-bot@kernel.org:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [High] NULL pointer dereference when enabling CRTC without a primary plane framebuffer.
False positive here. Calling drm_atomic_helper_check_primary_plane()
from the CRTC's atomic check makes it impossible to enable the CRTC
without a primary plane.
>
> Pre-existing issues:
> - [High] Unsafe direct access of global plane and CRTC state pointers in atomic commit callbacks.
> --
>
> commit 6344532446cab68117e44660f110ef0c8aec15d7
> Author: Ze Huang <ze.huang@oss.qualcomm.com>
>
> drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
>
> This commit replaces drm_simple_display_pipe with regular atomic helpers.
> It instantiates the plane, CRTC, and encoder directly, wiring them up with
> standard atomic helpers to remove the dependency on the simple-KMS interface.
>
>> diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c
>> index c93d61ac0bb75..375cdb79e4e89 100644
>> --- a/drivers/gpu/drm/tiny/arcpgu.c
>> +++ b/drivers/gpu/drm/tiny/arcpgu.c
> [ ... ]
>> @@ -117,7 +117,7 @@ static const u32 arc_pgu_supported_formats[] = {
>>
>> static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
>> {
>> - const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb;
>> + const struct drm_framebuffer *fb = arcpgu->plane.state->fb;
> [Severity: High]
> This isn't a bug introduced by this patch, but does reading the global
> plane->state pointer here instead of using the state passed via the
> drm_atomic_commit parameter cause issues during non-blocking atomic commits?
>
> If multiple non-blocking commits are queued rapidly, the global pointers
> advance to the newest state. When the first commit's commit_tail finally
> executes, reading plane->state directly instead of extracting the specific
> commit's state could cause the older commit to program the hardware using a
> future commit's state.
>
> [Severity: High]
> Can this dereference a NULL pointer if the CRTC is enabled without a primary
> plane framebuffer?
>
> The transition from drm_simple_display_pipe to generic atomic helpers removed
> the underlying protection that previously prevented the CRTC from enabling
> without a valid framebuffer. If userspace performs an atomic commit that
> enables the CRTC but sets the primary plane's framebuffer to 0 (NULL),
> drm_atomic_helper_check_crtc_primary_plane verifies the plane is attached but
> does not enforce plane_state->fb != NULL.
>
> This would allow the commit to proceed to arcpgu_crtc_helper_atomic_enable(),
> which calls arc_pgu_mode_set(), and then arc_pgu_set_pxl_fmt(), blindly
> dereferencing fb->format->format here.
>
>> uint32_t pixel_format = fb->format->format;
>> u32 format = DRM_FORMAT_INVALID;
>> int i;
> [ ... ]
>> @@ -155,7 +155,7 @@ static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *p
>>
>> static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
>> {
>> - struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode;
>> + struct drm_display_mode *m = &arcpgu->crtc.state->adjusted_mode;
> [Severity: High]
> This is a pre-existing issue, but could reading the global crtc->state pointer
> directly cause hardware state corruption or frame tearing?
>
> Similar to the plane state issue above, if rapid non-blocking atomic commits
> are generated, arc_pgu_mode_set() might read from the newest global state
> rather than the specific commit's state object.
>
>> u32 val;
>>
>> arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
> [ ... ]
>> @@ -218,35 +218,106 @@ static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
>> ~ARCPGU_CTRL_ENABLE_MASK);
>> }
>>
>> -static void arc_pgu_update(struct drm_simple_display_pipe *pipe,
>> - struct drm_plane_state *state)
>> +static void arcpgu_plane_helper_atomic_update(struct drm_plane *plane,
>> + struct drm_atomic_commit *state)
>> {
>> struct arcpgu_drm_private *arcpgu;
>> struct drm_gem_dma_object *gem;
>>
>> - if (!pipe->plane.state->fb)
>> + if (!plane->state->fb)
> [Severity: High]
> This is a pre-existing issue, but does arcpgu_plane_helper_atomic_update()
> safely handle non-blocking atomic commits when reading plane->state->fb
> directly?
>
> Reading the global plane->state pointer instead of extracting the new plane
> state from the drm_atomic_commit parameter could result in the same race
> condition during asynchronous commit_tail execution.
>
>> return;
>>
>> - arcpgu = pipe_to_arcpgu_priv(pipe);
>> - gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
>> + arcpgu = dev_to_arcpgu(plane->dev);
>> + gem = drm_fb_dma_get_gem_obj(plane->state->fb, 0);
>> arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->dma_addr);
>> }
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
2026-07-04 18:43 ` sashiko-bot
@ 2026-07-06 8:01 ` Thomas Zimmermann
1 sibling, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-07-06 8:01 UTC (permalink / raw)
To: Ze Huang, Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
David Airlie, Simona Vetter, Joel Stanley, Andrew Jeffery,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel
Hi
Am 04.07.26 um 20:31 schrieb Ze Huang:
> Instantiate plane, CRTC and encoder directly and wire them up with
> standard atomic helpers.
>
> This removes arcpgu's dependency on deprecated simple-KMS display pipe
> interface.
>
> Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
> ---
> drivers/gpu/drm/tiny/arcpgu.c | 165 +++++++++++++++++++++++++++++++++---------
> 1 file changed, 131 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c
> index c93d61ac0bb7..375cdb79e4e8 100644
> --- a/drivers/gpu/drm/tiny/arcpgu.c
> +++ b/drivers/gpu/drm/tiny/arcpgu.c
> @@ -17,12 +17,12 @@
> #include <drm/drm_fbdev_dma.h>
> #include <drm/drm_fourcc.h>
> #include <drm/drm_framebuffer.h>
> +#include <drm/drm_gem_atomic_helper.h>
> #include <drm/drm_gem_dma_helper.h>
> #include <drm/drm_gem_framebuffer_helper.h>
> #include <drm/drm_module.h>
> #include <drm/drm_of.h>
> #include <drm/drm_probe_helper.h>
> -#include <drm/drm_simple_kms_helper.h>
> #include <linux/dma-mapping.h>
> #include <linux/module.h>
> #include <linux/of_reserved_mem.h>
> @@ -52,14 +52,14 @@ struct arcpgu_drm_private {
> struct drm_device drm;
> void __iomem *regs;
> struct clk *clk;
> - struct drm_simple_display_pipe pipe;
> + struct drm_plane plane;
> + struct drm_crtc crtc;
> + struct drm_encoder encoder;
> struct drm_connector sim_conn;
> };
>
> #define dev_to_arcpgu(x) container_of(x, struct arcpgu_drm_private, drm)
>
> -#define pipe_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, pipe)
> -
> static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu,
> unsigned int reg, u32 value)
> {
> @@ -117,7 +117,7 @@ static const u32 arc_pgu_supported_formats[] = {
>
> static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
> {
> - const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb;
> + const struct drm_framebuffer *fb = arcpgu->plane.state->fb;
> uint32_t pixel_format = fb->format->format;
> u32 format = DRM_FORMAT_INVALID;
> int i;
> @@ -139,10 +139,10 @@ static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
> arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl);
> }
>
> -static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *pipe,
> - const struct drm_display_mode *mode)
> +static enum drm_mode_status arcpgu_crtc_helper_mode_valid(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode)
> {
> - struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
> + struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
> long rate, clk_rate = mode->clock * 1000;
> long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */
>
> @@ -155,7 +155,7 @@ static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *p
>
> static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
> {
> - struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode;
> + struct drm_display_mode *m = &arcpgu->crtc.state->adjusted_mode;
> u32 val;
>
> arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
> @@ -194,11 +194,10 @@ static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
> clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
> }
>
> -static void arc_pgu_enable(struct drm_simple_display_pipe *pipe,
> - struct drm_crtc_state *crtc_state,
> - struct drm_plane_state *plane_state)
> +static void arcpgu_crtc_helper_atomic_enable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
Since you're adding these functions anew, please use 'commit' for the
name of the drm_atomic_commit. Here and everywhere else in the series.
You don't have to update existing functions, of course.
The name 'state' is a bit off here and comes from when drm_atomic_commit
was still called drm_atomic_state.
CRTCs, plane, etc have state, but the update of these states is called
commit.
> {
> - struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
> + struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
>
> arc_pgu_mode_set(arcpgu);
>
> @@ -208,9 +207,10 @@ static void arc_pgu_enable(struct drm_simple_display_pipe *pipe,
> ARCPGU_CTRL_ENABLE_MASK);
> }
>
> -static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
> +static void arcpgu_crtc_helper_atomic_disable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
> + struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(crtc->dev);
>
> clk_disable_unprepare(arcpgu->clk);
> arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
> @@ -218,35 +218,106 @@ static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
> ~ARCPGU_CTRL_ENABLE_MASK);
> }
>
> -static void arc_pgu_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *state)
> +static void arcpgu_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> struct arcpgu_drm_private *arcpgu;
> struct drm_gem_dma_object *gem;
>
> - if (!pipe->plane.state->fb)
> + if (!plane->state->fb)
> return;
>
> - arcpgu = pipe_to_arcpgu_priv(pipe);
> - gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
> + arcpgu = dev_to_arcpgu(plane->dev);
> + gem = drm_fb_dma_get_gem_obj(plane->state->fb, 0);
> arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->dma_addr);
> }
>
> -static const struct drm_simple_display_pipe_funcs arc_pgu_pipe_funcs = {
> - .update = arc_pgu_update,
> - .mode_valid = arc_pgu_mode_valid,
> - .enable = arc_pgu_enable,
> - .disable = arc_pgu_disable,
> -};
> -
> static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
> - .fb_create = drm_gem_fb_create,
> + .fb_create = drm_gem_fb_create,
> .atomic_check = drm_atomic_helper_check,
> .atomic_commit = drm_atomic_helper_commit,
> };
>
> DEFINE_DRM_GEM_DMA_FOPS(arcpgu_drm_ops);
>
> +static int arcpgu_plane_helper_atomic_check(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> +{
> + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
> + struct drm_crtc *crtc = plane_state->crtc;
> + struct drm_crtc_state *crtc_state = NULL;
> + int ret;
> +
> + if (crtc)
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
> + DRM_PLANE_NO_SCALING,
> + DRM_PLANE_NO_SCALING,
> + false, false);
> + return ret;
Return directly here.
> +}
> +
> +static const struct drm_plane_helper_funcs arcpgu_plane_helper_funcs = {
> + .prepare_fb = drm_gem_plane_helper_prepare_fb,
> + .atomic_check = arcpgu_plane_helper_atomic_check,
> + .atomic_update = arcpgu_plane_helper_atomic_update,
> +};
> +
> +static bool arcpgu_plane_format_mod_supported(struct drm_plane *plane,
> + u32 format,
> + u64 modifier)
> +{
> + return modifier == DRM_FORMAT_MOD_LINEAR;
> +}
Please remove this function. It doesn't really do anything besides DRM's
standard behavior.
> +
> +static const struct drm_plane_funcs arcpgu_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,
> + .format_mod_supported = arcpgu_plane_format_mod_supported,
> +};
> +
> +static int arcpgu_crtc_helper_atomic_check(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> +{
> + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + int ret;
> +
> + if (!crtc_state->enable)
> + goto out;
> +
> + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
> + if (ret)
> + return ret;
> +
> +out:
> + return drm_atomic_add_affected_planes(state, crtc);
Instead of using out, I'd rather use
if (crtc->enable) {
//do checks
}
return add_affected planes.
Seems more natural to me.
> +}
> +
> +static const struct drm_crtc_helper_funcs arcpgu_crtc_helper_funcs = {
> + .mode_valid = arcpgu_crtc_helper_mode_valid,
> + .atomic_check = arcpgu_crtc_helper_atomic_check,
> + .atomic_enable = arcpgu_crtc_helper_atomic_enable,
> + .atomic_disable = arcpgu_crtc_helper_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs arcpgu_crtc_funcs = {
> + .reset = drm_atomic_helper_crtc_reset,
> + .destroy = drm_crtc_cleanup,
> + .set_config = drm_atomic_helper_set_config,
> + .page_flip = drm_atomic_helper_page_flip,
> + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +static const struct drm_encoder_funcs arcpgu_encoder_funcs = {
> + .destroy = drm_encoder_cleanup,
> +};
> +
> static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
> {
> struct platform_device *pdev = to_platform_device(arcpgu->drm.dev);
> @@ -254,6 +325,9 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
> struct device_node *endpoint_node = NULL;
> struct drm_connector *connector = NULL;
> struct drm_device *drm = &arcpgu->drm;
> + struct drm_plane *plane;
> + struct drm_encoder *encoder;
> + struct drm_crtc *crtc;
> int ret;
>
> arcpgu->clk = devm_clk_get(drm->dev, "pxlclk");
> @@ -301,12 +375,35 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
> return ret;
> }
>
> - ret = drm_simple_display_pipe_init(drm, &arcpgu->pipe, &arc_pgu_pipe_funcs,
> - arc_pgu_supported_formats,
> - ARRAY_SIZE(arc_pgu_supported_formats),
> - NULL, connector);
> + plane = &arcpgu->plane;
> + ret = drm_universal_plane_init(drm, plane, 0,
> + &arcpgu_plane_funcs,
> + arc_pgu_supported_formats,
> + ARRAY_SIZE(arc_pgu_supported_formats),
> + NULL,
> + DRM_PLANE_TYPE_PRIMARY, NULL);
> if (ret)
> return ret;
> + drm_plane_helper_add(plane, &arcpgu_plane_helper_funcs);
> +
> + crtc = &arcpgu->crtc;
> + ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
> + &arcpgu_crtc_funcs, NULL);
> + if (ret)
> + return ret;
> + drm_crtc_helper_add(crtc, &arcpgu_crtc_helper_funcs);
> +
> + encoder = &arcpgu->encoder;
> + ret = drm_encoder_init(drm, encoder, &arcpgu_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
> + if (ret)
> + return ret;
> + encoder->possible_crtcs = drm_crtc_mask(crtc);
I think plane, CRTC, and encoder should go before testing the
encoder_node at [1].
Then comes the encoder_node test. If true, do a single encoder-node
branch, or else to a single connector-based branch. That would
simplify the overall logic in this helper.
[1]
https://elixir.bootlin.com/linux/v7.1.2/source/drivers/gpu/drm/tiny/arcpgu.c#L287
> +
> + if (connector) {
> + ret = drm_connector_attach_encoder(connector, encoder);
> + if (ret)
> + return ret;
> + }
>
> if (encoder_node) {
> /* Locate drm bridge from the hdmi encoder DT node */
> @@ -315,7 +412,7 @@ static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
> if (!bridge)
> return -EPROBE_DEFER;
>
> - ret = drm_simple_display_pipe_attach_bridge(&arcpgu->pipe, bridge);
> + ret = drm_bridge_attach(encoder, bridge, NULL, 0);
> if (ret)
> return ret;
> }
> @@ -342,7 +439,7 @@ static int arcpgu_show_pxlclock(struct seq_file *m, void *arg)
> struct drm_device *drm = node->minor->dev;
> struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(drm);
> unsigned long clkrate = clk_get_rate(arcpgu->clk);
> - unsigned long mode_clock = arcpgu->pipe.crtc.mode.crtc_clock * 1000;
> + unsigned long mode_clock = arcpgu->crtc.mode.crtc_clock * 1000;
Here, mode is an obsolete field. The correct field is crtc->state->mode.
Best regards
Thomas
>
> seq_printf(m, "hw : %lu\n", clkrate);
> seq_printf(m, "mode: %lu\n", mode_clock);
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 0/9] drm: replace simple display pipe users with atomic helpers
2026-07-06 7:27 ` [PATCH 0/9] drm: replace simple display pipe users with " Thomas Zimmermann
@ 2026-07-06 8:22 ` Ze Huang
0 siblings, 0 replies; 23+ messages in thread
From: Ze Huang @ 2026-07-06 8:22 UTC (permalink / raw)
To: Thomas Zimmermann, Ze Huang, Alexey Brodkin, Maarten Lankhorst,
Maxime Ripard, David Airlie, Simona Vetter, Joel Stanley,
Andrew Jeffery, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel
On Mon Jul 6, 2026 at 3:27 PM CST, Thomas Zimmermann wrote:
> Hi
>
> Am 04.07.26 um 20:31 schrieb Ze Huang:
>> struct drm_simple_display_pipe was meant to simplify simple DRM
>> drivers, but instead adds an extra wrapper around normal DRM atomic
>> helper setup. As noted in Documentation/gpu/todo.rst, remaining users
>> should be converted to regular atomic helpers and stop depending on the
>> simple-KMS interfaces.
>>
>> This series converts the following drivers:
>>
>> - arcpgu
>> - aspeed
>> - imx lcdc
>> - mcde
>> - pl111
>> - gm12u320
>> - repaper
>> - tve200
>> - xen frontend
>>
>> Each patch replaces drm_simple_display_pipe_init() with explicit
>> primary plane, CRTC and encoder setup, and moves the old simple-pipe
>> callbacks into regular plane and CRTC helper callbacks named according
>> to local driver conventions.
>>
>> The conversions preserve helper behavior that used to be implicit in
>> drm_simple_kms_helper.c, including plane-state validation, CRTC
>> primary-plane checks, affected-plane propagation, framebuffer prepare
>> handling, and existing event/vblank flow where applicable.
>>
>> Result is less helper indirection and more explicit driver-side atomic
>> wiring, with no remaining simple-KMS dependency in these drivers.
>>
>> These changes are build-tested only. No hardware testing has been
>> performed on the affected devices.
>
> Thanks a lot for the series. That's quite a nice cleanup. Did you use
> any AI to create these patches?
>
Hi Thomas,
Yes, I did. I wrote the first two conversion patches (arcpgu and
aspeed) myself to understand the migration pattern. For the remaining
drivers, I used GPT-5.5 to help with the repetitive boilerplate
conversion.
I should have reviewed the generated code more carefully before sending
the series. The sashiko-bot feedback shows that I missed several important
details, including commit-local state handling, the implicit NULL fb /
visibility checks from simple-KMS, and vblank/pageflip event ordering.
I am now going through these issues more carefully and working out the
correct fixes before sending a v2.
Do you expect AI assistance to be mentioned in the cover letter or commit
messages in some specific form? If there is a preferred tag or wording
for this, I will use it in v2.
For issues that appear to be pre-existing but are exposed or carried over
by the migration, which is better?
1. Include them as separate prep/fix patches at the beginning of the v2
series, before the corresponding conversion patches; or
2. address those pre-existing issues in a separate follow-up series?
Thanks for your time and review. :)
> Best regards
> Thomas
>
Best regards,
Ze
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 2/9] drm/aspeed: replace struct drm_simple_display_pipe with regular atomic helpers
2026-07-04 18:31 ` [PATCH 2/9] drm/aspeed: " Ze Huang
2026-07-04 18:52 ` sashiko-bot
@ 2026-07-06 8:31 ` Thomas Zimmermann
1 sibling, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-07-06 8:31 UTC (permalink / raw)
To: Ze Huang, Alexey Brodkin, Maarten Lankhorst, Maxime Ripard,
David Airlie, Simona Vetter, Joel Stanley, Andrew Jeffery,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Linus Walleij, Hans de Goede, Alex Lanzano,
Oleksandr Andrushchenko
Cc: dri-devel, linux-kernel, linux-aspeed, linux-arm-kernel, imx,
xen-devel
Hi,
common points from my arcgpu review applied here as well. See below for
a new other things.
Am 04.07.26 um 20:31 schrieb Ze Huang:
> Replace simple display pipe with explicit plane, CRTC and encoder
> objects. Move callbacks to plane and CRTC helpers, with vblank handling
> through drm_crtc_funcs.
>
> This removes intermediate simple-pipe layer and uses standard atomic
> helper wiring.
>
> Signed-off-by: Ze Huang <ze.huang@oss.qualcomm.com>
> ---
> drivers/gpu/drm/aspeed/aspeed_gfx.h | 5 +-
> drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c | 156 +++++++++++++++++++++++--------
> drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 3 +-
> 3 files changed, 123 insertions(+), 41 deletions(-)
>
> diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h
> index 4e6a442c3886..a34811564c0d 100644
> --- a/drivers/gpu/drm/aspeed/aspeed_gfx.h
> +++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h
> @@ -2,7 +2,6 @@
> /* Copyright 2018 IBM Corporation */
>
> #include <drm/drm_device.h>
> -#include <drm/drm_simple_kms_helper.h>
>
> struct aspeed_gfx {
> struct drm_device drm;
> @@ -17,7 +16,9 @@ struct aspeed_gfx {
> u32 throd_val;
> u32 scan_line_max;
>
> - struct drm_simple_display_pipe pipe;
> + struct drm_plane plane;
> + struct drm_crtc crtc;
> + struct drm_encoder encoder;
> struct drm_connector connector;
> };
> #define to_aspeed_gfx(x) container_of(x, struct aspeed_gfx, drm)
> diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
> index 7877a57b8e26..3294795c31c4 100644
> --- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
> +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
> @@ -5,6 +5,8 @@
> #include <linux/reset.h>
> #include <linux/regmap.h>
>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> #include <drm/drm_device.h>
> #include <drm/drm_fb_dma_helper.h>
> #include <drm/drm_fourcc.h>
> @@ -12,20 +14,13 @@
> #include <drm/drm_gem_atomic_helper.h>
> #include <drm/drm_gem_dma_helper.h>
> #include <drm/drm_panel.h>
> -#include <drm/drm_simple_kms_helper.h>
> #include <drm/drm_vblank.h>
>
> #include "aspeed_gfx.h"
>
> -static struct aspeed_gfx *
> -drm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
> -{
> - return container_of(pipe, struct aspeed_gfx, pipe);
> -}
> -
Please create a new helper
struct drm_aspeed_gfx *to_aspeed_gfx(drm_device *drm)
that does the upcast.
> static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
> {
> - struct drm_crtc *crtc = &priv->pipe.crtc;
> + struct drm_crtc *crtc = &priv->crtc;
> struct drm_device *drm = crtc->dev;
> const u32 format = crtc->primary->state->fb->format->format;
> u32 ctrl1;
> @@ -79,7 +74,7 @@ static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
>
> static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
> {
> - struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
> + struct drm_display_mode *m = &priv->crtc.state->adjusted_mode;
> u32 ctrl1, d_offset, t_count, bpp;
> int err;
>
> @@ -139,33 +134,31 @@ static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
> writel(priv->throd_val, priv->base + CRT_THROD);
> }
>
> -static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
> - struct drm_crtc_state *crtc_state,
> - struct drm_plane_state *plane_state)
> +static void aspeed_gfx_crtc_helper_atomic_enable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
Please see my comment on arcgpu for the new naming of 'state'.
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> + struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
Please use your helper to_aspeed_gfx(crtc->dev) to do the upcast.
Here any in other places.
>
> aspeed_gfx_crtc_mode_set_nofb(priv);
> aspeed_gfx_enable_controller(priv);
> drm_crtc_vblank_on(crtc);
> }
>
> -static void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
> +static void aspeed_gfx_crtc_helper_atomic_disable(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> + struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
Another upcast issue
>
> drm_crtc_vblank_off(crtc);
> aspeed_gfx_disable_controller(priv);
> }
>
> -static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
> - struct drm_plane_state *plane_state)
> +static void aspeed_gfx_plane_helper_atomic_update(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> - struct drm_crtc *crtc = &pipe->crtc;
> - struct drm_framebuffer *fb = pipe->plane.state->fb;
> + struct aspeed_gfx *priv = container_of(plane, struct aspeed_gfx, plane);
to_aspeed_gfx(plane->dev)
> + struct drm_crtc *crtc = &priv->crtc;
> + struct drm_framebuffer *fb = plane->state->fb;
> struct drm_pending_vblank_event *event;
> struct drm_gem_dma_object *gem;
>
> @@ -190,9 +183,9 @@ static void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
> writel(gem->dma_addr, priv->base + CRT_ADDR);
> }
>
> -static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
> +static int aspeed_gfx_crtc_enable_vblank(struct drm_crtc *crtc)
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> + struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
> u32 reg = readl(priv->base + CRT_CTRL1);
>
> /* Clear pending VBLANK IRQ */
> @@ -204,9 +197,9 @@ static int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
> return 0;
> }
>
> -static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
> +static void aspeed_gfx_crtc_disable_vblank(struct drm_crtc *crtc)
> {
> - struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
> + struct aspeed_gfx *priv = container_of(crtc, struct aspeed_gfx, crtc);
> u32 reg = readl(priv->base + CRT_CTRL1);
>
> reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
> @@ -216,12 +209,75 @@ static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
> writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
> }
>
> -static const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
> - .enable = aspeed_gfx_pipe_enable,
> - .disable = aspeed_gfx_pipe_disable,
> - .update = aspeed_gfx_pipe_update,
> - .enable_vblank = aspeed_gfx_enable_vblank,
> - .disable_vblank = aspeed_gfx_disable_vblank,
> +static int aspeed_gfx_plane_helper_atomic_check(struct drm_plane *plane,
> + struct drm_atomic_commit *state)
> +{
> + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
> + struct drm_crtc *crtc = plane_state->crtc;
> + struct drm_crtc_state *crtc_state = NULL;
> + int ret;
> +
> + if (crtc)
> + crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> +
> + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
> + DRM_PLANE_NO_SCALING,
> + DRM_PLANE_NO_SCALING,
> + false, false);
> + return ret;
> +}
Return directly.
> +
> +static const struct drm_plane_helper_funcs aspeed_gfx_plane_helper_funcs = {
> + .prepare_fb = drm_gem_plane_helper_prepare_fb,
> + .atomic_check = aspeed_gfx_plane_helper_atomic_check,
> + .atomic_update = aspeed_gfx_plane_helper_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs aspeed_gfx_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 aspeed_gfx_crtc_helper_atomic_check(struct drm_crtc *crtc,
> + struct drm_atomic_commit *state)
> +{
> + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + int ret;
> +
> + if (!crtc_state->enable)
> + goto out;
> +
> + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
> + if (ret)
> + return ret;
> +
> +out:
> + return drm_atomic_add_affected_planes(state, crtc);
> +}
See arcpgu on a possible style improvement.
Best regards
Thomas
> +
> +static const struct drm_crtc_helper_funcs aspeed_gfx_crtc_helper_funcs = {
> + .atomic_check = aspeed_gfx_crtc_helper_atomic_check,
> + .atomic_enable = aspeed_gfx_crtc_helper_atomic_enable,
> + .atomic_disable = aspeed_gfx_crtc_helper_atomic_disable,
> +};
> +
> +static const struct drm_crtc_funcs aspeed_gfx_crtc_funcs = {
> + .reset = drm_atomic_helper_crtc_reset,
> + .destroy = drm_crtc_cleanup,
> + .set_config = drm_atomic_helper_set_config,
> + .page_flip = drm_atomic_helper_page_flip,
> + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> + .enable_vblank = aspeed_gfx_crtc_enable_vblank,
> + .disable_vblank = aspeed_gfx_crtc_disable_vblank,
> +};
> +
> +static const struct drm_encoder_funcs aspeed_gfx_encoder_funcs = {
> + .destroy = drm_encoder_cleanup,
> };
>
> static const uint32_t aspeed_gfx_formats[] = {
> @@ -232,10 +288,36 @@ static const uint32_t aspeed_gfx_formats[] = {
> int aspeed_gfx_create_pipe(struct drm_device *drm)
> {
> struct aspeed_gfx *priv = to_aspeed_gfx(drm);
> + struct drm_plane *plane = &priv->plane;
> + struct drm_crtc *crtc = &priv->crtc;
> + struct drm_encoder *encoder = &priv->encoder;
> + int ret;
> +
> + ret = drm_universal_plane_init(drm, plane, 0,
> + &aspeed_gfx_plane_funcs,
> + aspeed_gfx_formats,
> + ARRAY_SIZE(aspeed_gfx_formats),
> + NULL,
> + DRM_PLANE_TYPE_PRIMARY, NULL);
> + if (ret)
> + return ret;
> + drm_plane_helper_add(plane, &aspeed_gfx_plane_helper_funcs);
> +
> + ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL,
> + &aspeed_gfx_crtc_funcs, NULL);
> + if (ret)
> + return ret;
> + drm_crtc_helper_add(crtc, &aspeed_gfx_crtc_helper_funcs);
> +
> + ret = drm_encoder_init(drm, encoder, &aspeed_gfx_encoder_funcs,
> + DRM_MODE_ENCODER_NONE, NULL);
> + if (ret)
> + return ret;
> + encoder->possible_crtcs = drm_crtc_mask(crtc);
> +
> + ret = drm_connector_attach_encoder(&priv->connector, encoder);
> + if (ret)
> + return ret;
>
> - return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
> - aspeed_gfx_formats,
> - ARRAY_SIZE(aspeed_gfx_formats),
> - NULL,
> - &priv->connector);
> + return 0;
> }
> diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
> index 46094cca2974..b2d805f0c16d 100644
> --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
> +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c
> @@ -21,7 +21,6 @@
> #include <drm/drm_gem_framebuffer_helper.h>
> #include <drm/drm_module.h>
> #include <drm/drm_probe_helper.h>
> -#include <drm/drm_simple_kms_helper.h>
> #include <drm/drm_vblank.h>
> #include <drm/drm_drv.h>
>
> @@ -130,7 +129,7 @@ static irqreturn_t aspeed_gfx_irq_handler(int irq, void *data)
> reg = readl(priv->base + CRT_CTRL1);
>
> if (reg & CRT_CTRL_VERTICAL_INTR_STS) {
> - drm_crtc_handle_vblank(&priv->pipe.crtc);
> + drm_crtc_handle_vblank(&priv->crtc);
> writel(reg, priv->base + priv->int_clr_reg);
> return IRQ_HANDLED;
> }
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-07-06 8:31 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-04 18:31 [PATCH 0/9] drm: replace simple display pipe users with atomic helpers Ze Huang
2026-07-04 18:31 ` [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular " Ze Huang
2026-07-04 18:43 ` sashiko-bot
2026-07-06 7:43 ` Thomas Zimmermann
2026-07-06 8:01 ` Thomas Zimmermann
2026-07-04 18:31 ` [PATCH 2/9] drm/aspeed: " Ze Huang
2026-07-04 18:52 ` sashiko-bot
2026-07-06 8:31 ` Thomas Zimmermann
2026-07-04 18:31 ` [PATCH 3/9] drm/imx: " Ze Huang
2026-07-04 18:46 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 4/9] drm/mcde: " Ze Huang
2026-07-04 18:44 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 5/9] drm/pl111: " Ze Huang
2026-07-04 18:45 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 6/9] drm/gm12u320: " Ze Huang
2026-07-04 18:45 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 7/9] drm/repaper: " Ze Huang
2026-07-04 18:31 ` [PATCH 8/9] drm/tve200: " Ze Huang
2026-07-04 18:46 ` sashiko-bot
2026-07-04 18:31 ` [PATCH 9/9] drm/xen: " Ze Huang
2026-07-04 18:53 ` sashiko-bot
2026-07-06 7:27 ` [PATCH 0/9] drm: replace simple display pipe users with " Thomas Zimmermann
2026-07-06 8:22 ` Ze Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox