* [PATCH v1 1/2] drm/mipi-dbi: honour the plane source offset when flushing
2026-09-10 18:48 [PATCH v1 0/2] drm/mipi-dbi: display a cropped region of an oversized framebuffer Jonathan Frazin
@ 2026-09-10 18:48 ` Jonathan Frazin
2026-09-10 18:48 ` [PATCH v1 2/2] drm/tiny: allow a framebuffer larger than the panel on MIPI DBI drivers Jonathan Frazin
1 sibling, 0 replies; 4+ messages in thread
From: Jonathan Frazin @ 2026-09-10 18:48 UTC (permalink / raw)
To: dri-devel
Cc: Thomas Zimmermann, Maxime Ripard, Maarten Lankhorst,
David Airlie, Simona Vetter, Kamlesh Gurudasani, Alex Lanzano,
linux-kernel, Jonathan Frazin, Dave Stevenson
mipi_dbi_fb_dirty() takes the damage rectangle from
drm_atomic_helper_damage_merged(), which is expressed in framebuffer
coordinates and already clipped to the plane's source rectangle. It
then passed that rectangle straight to mipi_dbi_set_window_address(),
which is correct only while the source rectangle starts at (0,0) - i.e.
while the framebuffer is exactly panel-sized.
If a driver allows a framebuffer larger than the panel and the plane
selects a sub-region with a non-zero src_x/src_y, the controller was
still addressed in framebuffer coordinates, so the wrong part of the
panel was written and an out-of-range window could be programmed.
Pass the integer plane source origin down to mipi_dbi_fb_dirty() and
subtract it when programming the column/page address. The copy into the
transfer buffer still uses the framebuffer-coordinate rectangle, so it
keeps reading the correct pixels from an oversized source. With a
panel-sized framebuffer src_x/src_y are zero and behaviour is unchanged.
Cc: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Jonathan Frazin <frazinjonathan@gmail.com>
---
drivers/gpu/drm/drm_mipi_dbi.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c
index 25cf04d02..38db45a9d 100644
--- a/drivers/gpu/drm/drm_mipi_dbi.c
+++ b/drivers/gpu/drm/drm_mipi_dbi.c
@@ -271,7 +271,8 @@ static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev,
}
static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
- struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state)
+ struct drm_rect *rect, unsigned int src_x, unsigned int src_y,
+ struct drm_format_conv_state *fmtcnv_state)
{
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
unsigned int height = rect->y2 - rect->y1;
@@ -298,8 +299,13 @@ static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
tr = src->vaddr; /* TODO: Use mapping abstraction properly */
}
- mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1,
- rect->y2 - 1);
+ /*
+ * @rect is in framebuffer coordinates and has been clipped to the plane
+ * src rectangle by the damage iterator. The panel is addressed relative
+ * to the src origin, so subtract it here.
+ */
+ mipi_dbi_set_window_address(dbidev, rect->x1 - src_x, rect->x2 - 1 - src_x,
+ rect->y1 - src_y, rect->y2 - 1 - src_y);
if (fb->format->format == DRM_FORMAT_XRGB8888)
dst_format = drm_format_info(dbidev->pixel_format);
@@ -390,6 +396,8 @@ void drm_mipi_dbi_plane_helper_atomic_update(struct drm_plane *plane,
if (drm_dev_enter(plane->dev, &idx)) {
if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &rect))
mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
+ plane_state->src_x >> 16,
+ plane_state->src_y >> 16,
&shadow_plane_state->fmtcnv_state);
drm_dev_exit(idx);
}
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v1 2/2] drm/tiny: allow a framebuffer larger than the panel on MIPI DBI drivers
2026-09-10 18:48 [PATCH v1 0/2] drm/mipi-dbi: display a cropped region of an oversized framebuffer Jonathan Frazin
2026-09-10 18:48 ` [PATCH v1 1/2] drm/mipi-dbi: honour the plane source offset when flushing Jonathan Frazin
@ 2026-09-10 18:48 ` Jonathan Frazin
[not found] ` <20260910190542.BE6AE1F000FF@smtp.kernel.org>
1 sibling, 1 reply; 4+ messages in thread
From: Jonathan Frazin @ 2026-09-10 18:48 UTC (permalink / raw)
To: dri-devel
Cc: Thomas Zimmermann, Maxime Ripard, Maarten Lankhorst,
David Airlie, Simona Vetter, Kamlesh Gurudasani, Alex Lanzano,
linux-kernel, Jonathan Frazin, Dave Stevenson
Every drm/tiny MIPI DBI driver pins mode_config.max_width/max_height to
the panel size, so KMS rejects any framebuffer that is not exactly
panel-sized:
ili9341 spi0.0: bad framebuffer width 480, should be >= 240 && <= 240
Raise the maximums to DRM_SHADOW_PLANE_MAX_WIDTH/HEIGHT (which its
kerneldoc recommends for shadow-plane drivers) on the drivers that flush
through the shared drm_mipi_dbi_plane_helper_atomic_update(), so a client
can allocate a larger framebuffer and pick the displayed region through
the plane source rectangle - a crop / pan with no scaling, now that
drm_mipi_dbi honours the source offset (previous patch):
ili9341, hx8357d, ili9486, mi0283qt, ili9163, panel-mipi-dbi
The fixed mode, the minimums and the connector are unchanged. The plane
check (drm_mipi_dbi_plane_helper_atomic_check) still forbids scaling and
repositioning, and the transfer buffer is sized from the display mode,
so the flushed rectangle stays bounded by the panel regardless of the
framebuffer dimensions.
ili9225 is left out: it has its own atomic_update / ili9225_fb_dirty()
that addresses the panel from the damage rectangle without the source
offset, so raising its limits would let a mispositioned buffer through.
Cc: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Jonathan Frazin <frazinjonathan@gmail.com>
---
drivers/gpu/drm/tiny/hx8357d.c | 8 ++++++--
drivers/gpu/drm/tiny/ili9163.c | 8 ++++++--
drivers/gpu/drm/tiny/ili9341.c | 8 ++++++--
drivers/gpu/drm/tiny/ili9486.c | 8 ++++++--
drivers/gpu/drm/tiny/mi0283qt.c | 8 ++++++--
drivers/gpu/drm/tiny/panel-mipi-dbi.c | 8 ++++++--
6 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c
index f942a8d09..0599b8ccf 100644
--- a/drivers/gpu/drm/tiny/hx8357d.c
+++ b/drivers/gpu/drm/tiny/hx8357d.c
@@ -320,9 +320,13 @@ static int hx8357d_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &hx8357d_mode_config_funcs;
drm->mode_config.preferred_depth = 16;
drm->mode_config.helper_private = &hx8357d_mode_config_helper_funcs;
diff --git a/drivers/gpu/drm/tiny/ili9163.c b/drivers/gpu/drm/tiny/ili9163.c
index 884242450..fe6c13056 100644
--- a/drivers/gpu/drm/tiny/ili9163.c
+++ b/drivers/gpu/drm/tiny/ili9163.c
@@ -251,9 +251,13 @@ static int ili9163_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &ili9163_mode_config_funcs;
drm->mode_config.preferred_depth = 16;
drm->mode_config.helper_private = &ili9163_mode_config_helper_funcs;
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c
index 003381aa2..1e4bb56e3 100644
--- a/drivers/gpu/drm/tiny/ili9341.c
+++ b/drivers/gpu/drm/tiny/ili9341.c
@@ -282,9 +282,13 @@ static int ili9341_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &ili9341_mode_config_funcs;
drm->mode_config.preferred_depth = 16;
drm->mode_config.helper_private = &ili9341_mode_config_helper_funcs;
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c
index fab8bd7ce..de33d702a 100644
--- a/drivers/gpu/drm/tiny/ili9486.c
+++ b/drivers/gpu/drm/tiny/ili9486.c
@@ -309,9 +309,13 @@ static int ili9486_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &ili9486_mode_config_funcs;
drm->mode_config.preferred_depth = 16;
drm->mode_config.helper_private = &ili9486_mode_config_helper_funcs;
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c
index b0121e89e..df3801914 100644
--- a/drivers/gpu/drm/tiny/mi0283qt.c
+++ b/drivers/gpu/drm/tiny/mi0283qt.c
@@ -290,9 +290,13 @@ static int mi0283qt_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &mi0283qt_mode_config_funcs;
drm->mode_config.preferred_depth = 16;
drm->mode_config.helper_private = &mi0283qt_mode_config_helper_funcs;
diff --git a/drivers/gpu/drm/tiny/panel-mipi-dbi.c b/drivers/gpu/drm/tiny/panel-mipi-dbi.c
index 60cd65cae..a0a098b0d 100644
--- a/drivers/gpu/drm/tiny/panel-mipi-dbi.c
+++ b/drivers/gpu/drm/tiny/panel-mipi-dbi.c
@@ -448,9 +448,13 @@ static int panel_mipi_dbi_spi_probe(struct spi_device *spi)
return ret;
drm->mode_config.min_width = dbidev->mode.hdisplay;
- drm->mode_config.max_width = dbidev->mode.hdisplay;
+ /*
+ * Allow a framebuffer larger than the panel so a sub-region can be
+ * displayed via the plane source rectangle (crop / pan, no scaling).
+ */
+ drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH;
drm->mode_config.min_height = dbidev->mode.vdisplay;
- drm->mode_config.max_height = dbidev->mode.vdisplay;
+ drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT;
drm->mode_config.funcs = &panel_mipi_dbi_mode_config_funcs;
drm->mode_config.preferred_depth = bpp;
drm->mode_config.helper_private = &panel_mipi_dbi_mode_config_helper_funcs;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread