Re: [PATCH 1/9] drm/arcpgu: replace struct drm_simple_display_pipe with regular atomic helpers
"Ze Huang" <[email protected]> Mon, 06 Jul 2026 21:26:52 +0800
| Newsgroups | org.ozlabs.lists.linux-aspeed,dev.linux.lists.imx,org.freedesktop.lists.dri-devel,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.xenproject.lists.xen-devel |
|---|---|
| Message-ID | <[email protected]> |
On Mon Jul 6, 2026 at 4:01 PM CST, Thomas Zimmermann wrote: > 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 <[email protected]> >> --- >> 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. > Thanks for your explanation. I will change to name 'commit' for type drm_atomic_commit, here and everywhere else in the series. > >> { >> - 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. > OK > >> +} >> + >> +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. > Will do >> + >> +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. > Will do >> +} >> + >> +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 > OK, will move them before the encoder_node test > >> + >> + 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. Will correct the field > > Best regards > Thomas > >> >> seq_printf(m, "hw : %lu\n", clkrate); >> seq_printf(m, "mode: %lu\n", mode_clock); >> Thanks, Ze