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

Reply via email to