QXL packs per-CRTC dumb buffers into a single primary surface.
qxl_update_dumb_head() recorded each dumb BO allocation (bo->surf)
instead of the plane source rectangle. Scanning 1280x800 from a
2048x1024 dumb framebuffer beside a 1024x768 head therefore created
a 3072x1024 primary and placed head 1 at +2048, rather than 2304x800
with head 1 at +1280.

Use src_w/src_h when building the packed shadow, and copy only that
source rectangle into it.

Fixes: 90adda2ce898 ("drm/qxl: cover all crtcs in shadow bo.")
Assisted-by: LLM sparse
Signed-off-by: Dillon Amburgey <[email protected]>
---
Tested on torvalds/linux 62f4c998b297. A DRM client (not Xorg, not
SPICE) programmed one QXL device with max_outputs=2. CRTC 0 scans a
1280x800 rectangle from a 2048x1024 dumb framebuffer (the allocation
is larger than the scanout). CRTC 1 scans a separate 1024x768 dumb
framebuffer. Unpatched, qxl_update_dumb_head() sizes packed heads
from bo->surf, so QEMU's qxl_create_guest_primary is 3072x1024
(2048+1024 by max height) and monitors_config places head 1 at +2048.
With this patch, the primary is 2304x800 (1280+1024) and head 1 is at
+1280.

Content outside CRTC 0's 1280x800 source rectangle did not appear in
the packed primary, and both heads' source rectangles did.

checkpatch.pl --strict: 0 errors, 0 warnings, 0 checks. W=1 and Sparse
on drivers/gpu/drm/qxl/qxl_display.c added no warnings.

 drivers/gpu/drm/qxl/qxl_display.c | 39 ++++++++++++++++---------------
 1 file changed, 20 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/qxl/qxl_display.c 
b/drivers/gpu/drm/qxl/qxl_display.c
index 0719fc6a52d5..e57aeeb97f84 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -670,13 +670,17 @@ static void qxl_primary_atomic_update(struct drm_plane 
*plane,
        struct qxl_device *qdev = to_qxl(plane->dev);
        struct qxl_bo *bo = gem_to_qxl_bo(new_state->fb->obj[0]);
        struct qxl_bo *primary;
-       struct drm_clip_rect norect = {
-           .x1 = 0,
-           .y1 = 0,
-           .x2 = new_state->fb->width,
-           .y2 = new_state->fb->height
-       };
+       struct drm_clip_rect norect;
        uint32_t dumb_shadow_offset = 0;
+       u32 src_x = new_state->src_x >> 16;
+       u32 src_y = new_state->src_y >> 16;
+       u32 src_w = new_state->src_w >> 16;
+       u32 src_h = new_state->src_h >> 16;
+
+       norect.x1 = src_x;
+       norect.y1 = src_y;
+       norect.x2 = src_x + src_w;
+       norect.y2 = src_y + src_h;
 
        primary = bo->shadow ? bo->shadow : bo;
 
@@ -689,7 +693,7 @@ static void qxl_primary_atomic_update(struct drm_plane 
*plane,
 
        if (bo->is_dumb)
                dumb_shadow_offset =
-                       qdev->dumb_heads[new_state->crtc->index].x;
+                       qdev->dumb_heads[new_state->crtc->index].x - src_x;
 
        qxl_draw_dirty_fb(qdev, new_state->fb, bo, 0, 0, &norect, 1, 1,
                          dumb_shadow_offset);
@@ -764,18 +768,14 @@ static void qxl_cursor_atomic_disable(struct drm_plane 
*plane,
        qcrtc->cursor_bo = NULL;
 }
 
-static void qxl_update_dumb_head(struct qxl_device *qdev,
-                                int index, struct qxl_bo *bo)
+static void qxl_update_dumb_head(struct qxl_device *qdev, int index,
+                                struct qxl_bo *bo, uint32_t width,
+                                uint32_t height)
 {
-       uint32_t width, height;
-
        if (index >= qdev->monitors_config->max_allowed)
                return;
 
-       if (bo && bo->is_dumb) {
-               width = bo->surf.width;
-               height = bo->surf.height;
-       } else {
+       if (!bo || !bo->is_dumb) {
                width = 0;
                height = 0;
        }
@@ -820,12 +820,11 @@ static void qxl_calc_dumb_shadow(struct qxl_device *qdev,
 }
 
 static void qxl_prepare_shadow(struct qxl_device *qdev, struct qxl_bo *user_bo,
-                              int crtc_index)
+                              int crtc_index, uint32_t width, uint32_t height)
 {
        struct qxl_surface surf;
 
-       qxl_update_dumb_head(qdev, crtc_index,
-                            user_bo);
+       qxl_update_dumb_head(qdev, crtc_index, user_bo, width, height);
        qxl_calc_dumb_shadow(qdev, &surf);
        if (!qdev->dumb_shadow_bo ||
            qdev->dumb_shadow_bo->surf.width  != surf.width ||
@@ -869,7 +868,9 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane,
 
        if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
            user_bo->is_dumb) {
-               qxl_prepare_shadow(qdev, user_bo, new_state->crtc->index);
+               qxl_prepare_shadow(qdev, user_bo, new_state->crtc->index,
+                                  new_state->src_w >> 16,
+                                  new_state->src_h >> 16);
        }
 
        if (plane->type == DRM_PLANE_TYPE_CURSOR &&

base-commit: 62f4c998b297cf233997a2b4cd6fc2d2df0319c9
-- 
2.43.0


Reply via email to