From 88dcc1ae855a796720a2feb3727b359885507c16 Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Mon, 31 Aug 2026 20:35:43 -0500 Subject: [PATCH 1/4] drm/mipi-dbi: honour the plane source offset when flushing 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. Signed-off-by: Jonathan Frazin --- 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 25cf04d029f77e..38db45a9dbc35e 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); } From 63c6acd150cd7fdf9cfc98e48d16e5415c4e09f4 Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Mon, 31 Aug 2026 20:35:45 -0500 Subject: [PATCH 2/4] drm/tiny/ili9341: allow a framebuffer larger than the panel The driver set mode_config.max_width/max_height equal to the panel dimensions, so KMS rejected any framebuffer that was not exactly panel-sized: ili9341 spi0.0: bad framebuffer width 480, should be >= 240 && <= 240 Raise the maximums so userspace can allocate a larger framebuffer and choose the displayed region through the plane's source rectangle - a crop / pan with no scaling. The minimums, the fixed display mode and the connector are unchanged, and drm_mipi_dbi now translates the source offset when addressing the controller. The transfer buffer is sized from the display mode, and the plane check (drm_mipi_dbi_plane_helper_atomic_check) forbids scaling and repositioning, so the flushed rectangle stays bounded by the panel size regardless of the framebuffer dimensions. Signed-off-by: Jonathan Frazin --- drivers/gpu/drm/tiny/ili9341.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 003381aa22ea4f..6ca66e1a46bad3 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; drm->mode_config.funcs = &ili9341_mode_config_funcs; drm->mode_config.preferred_depth = 16; drm->mode_config.helper_private = &ili9341_mode_config_helper_funcs; From edcf2f22d95dcdc2bb0c21d20a2f0da5a9f7309b Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Tue, 1 Sep 2026 18:33:11 -0500 Subject: [PATCH 3/4] drm/tiny/hx8357d: allow a framebuffer larger than the panel Same change as the preceding ili9341 patch: mode_config.max_width/height were pinned to the panel dimensions, rejecting any framebuffer that was not exactly panel-sized. Raise them so a sub-region of a larger framebuffer can be displayed via the plane source rectangle, now that drm_mipi_dbi translates the source offset. The fixed mode, the minimums and the connector are unchanged; the plane check forbids scaling and repositioning and tx_buf is sized from the mode, so the flushed rectangle stays bounded by the panel. Compile-tested only; the functional testing was done on ili9341. Signed-off-by: Jonathan Frazin --- drivers/gpu/drm/tiny/hx8357d.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index f942a8d09ff229..1345cf71cf5c31 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; drm->mode_config.funcs = &hx8357d_mode_config_funcs; drm->mode_config.preferred_depth = 16; drm->mode_config.helper_private = &hx8357d_mode_config_helper_funcs; From 8c983558e00c0090b2bda6a5bc91c595de12a4e1 Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Tue, 8 Sep 2026 18:03:26 -0500 Subject: [PATCH 4/4] drm/tiny: allow oversized framebuffers on the remaining mipi-dbi drivers Extend the ili9341/hx8357d change to every other drm/tiny SPI driver that flushes through the shared DRM_MIPI_DBI_PLANE_HELPER_FUNCS, so a client can scan out a cropped sub-region of a larger framebuffer on any of them: ili9486, panel-mipi-dbi, mi0283qt, ili9163 panel-mipi-dbi is the generic driver used for ST7789 and other MIPI DBI controllers without a dedicated driver, so this covers most SPI TFT breakouts in practice. All four use drm_mipi_dbi_plane_helper_atomic_update() unchanged, which now translates the plane source offset (see "drm/mipi-dbi: honour the plane source offset when flushing"). The fixed mode, minimums, connector and mode-sized transfer buffer are untouched, and the plane check still forbids scaling and repositioning, so the flushed rectangle stays bounded by the panel. ili9225 is deliberately excluded: 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. Only ili9341 has been tested on hardware; the other four are compile- tested only. Signed-off-by: Jonathan Frazin Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YCFunyWDqtsV58BGexkwUE --- drivers/gpu/drm/tiny/ili9163.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 ++++++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tiny/ili9163.c b/drivers/gpu/drm/tiny/ili9163.c index 8842424504e3fb..9b0cdab8f48ccd 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; 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/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index fab8bd7cecd17e..ccaf933eb91f25 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; 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 b0121e89e2dc0e..630cd4d2b8fd9b 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; 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 60cd65cae41d8d..8f2d701569f25e 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 + * selected via the plane src rectangle (crop / pan with no scaling). + */ + drm->mode_config.max_width = 4096; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = 4096; 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;