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 <[email protected]> Signed-off-by: Jonathan Frazin <[email protected]> --- 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
