Commit 90adda2c authored by Gerd Hoffmann's avatar Gerd Hoffmann

drm/qxl: cover all crtcs in shadow bo.

The qxl device supports only a single active framebuffer ("primary
surface" in spice terminology).  In multihead configurations are handled
by defining rectangles within the primary surface for each head/crtc.

Userspace which uses the qxl ioctl interface (xorg qxl driver) is aware
of this limitation and will setup framebuffers and crtcs accordingly.

Userspace which uses dumb framebuffers (xorg modesetting driver,
wayland) is not aware of this limitation and tries to use two
framebuffers (one for each crtc) instead.

The qxl kms driver already has the dumb bo separated from the primary
surface, by using a (shared) shadow bo as primary surface.  This is
needed to support pageflips without having to re-create the primary
surface.  The qxl driver will blit from the dumb bo to the shadow bo
instead.

So we can extend the shadow logic:  Maintain a global shadow bo (aka
primary surface), make it big enough that dumb bo's for all crtcs fit in
side-by-side.  Adjust the pageflip blits to place the heads next to each
other in the shadow.

With this patch in place multihead qxl works with wayland.
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
Acked-by: default avatarNoralf Trønnes <noralf@tronnes.org>
Link: http://patchwork.freedesktop.org/patch/msgid/20190118122020.27596-15-kraxel@redhat.com
parent 4979904c
...@@ -323,6 +323,8 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc, ...@@ -323,6 +323,8 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
head.y = crtc->y; head.y = crtc->y;
if (qdev->monitors_config->count < i + 1) if (qdev->monitors_config->count < i + 1)
qdev->monitors_config->count = i + 1; qdev->monitors_config->count = i + 1;
if (qdev->primary_bo == qdev->dumb_shadow_bo)
head.x += qdev->dumb_heads[i].x;
} else if (i > 0) { } else if (i > 0) {
head.width = 0; head.width = 0;
head.height = 0; head.height = 0;
...@@ -426,7 +428,7 @@ static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb, ...@@ -426,7 +428,7 @@ static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
} }
qxl_draw_dirty_fb(qdev, fb, qobj, flags, color, qxl_draw_dirty_fb(qdev, fb, qobj, flags, color,
clips, num_clips, inc); clips, num_clips, inc, 0);
drm_modeset_unlock_all(fb->dev); drm_modeset_unlock_all(fb->dev);
...@@ -535,6 +537,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane, ...@@ -535,6 +537,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
.x2 = plane->state->fb->width, .x2 = plane->state->fb->width,
.y2 = plane->state->fb->height .y2 = plane->state->fb->height
}; };
uint32_t dumb_shadow_offset = 0;
if (old_state->fb) { if (old_state->fb) {
bo_old = gem_to_qxl_bo(old_state->fb->obj[0]); bo_old = gem_to_qxl_bo(old_state->fb->obj[0]);
...@@ -551,7 +554,12 @@ static void qxl_primary_atomic_update(struct drm_plane *plane, ...@@ -551,7 +554,12 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
qxl_primary_apply_cursor(plane); qxl_primary_apply_cursor(plane);
} }
qxl_draw_dirty_fb(qdev, plane->state->fb, bo, 0, 0, &norect, 1, 1); if (bo->is_dumb)
dumb_shadow_offset =
qdev->dumb_heads[plane->state->crtc->index].x;
qxl_draw_dirty_fb(qdev, plane->state->fb, bo, 0, 0, &norect, 1, 1,
dumb_shadow_offset);
} }
static void qxl_primary_atomic_disable(struct drm_plane *plane, static void qxl_primary_atomic_disable(struct drm_plane *plane,
...@@ -707,12 +715,68 @@ static void qxl_cursor_atomic_disable(struct drm_plane *plane, ...@@ -707,12 +715,68 @@ static void qxl_cursor_atomic_disable(struct drm_plane *plane,
qxl_release_fence_buffer_objects(release); qxl_release_fence_buffer_objects(release);
} }
static void qxl_update_dumb_head(struct qxl_device *qdev,
int index, struct qxl_bo *bo)
{
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 {
width = 0;
height = 0;
}
if (qdev->dumb_heads[index].width == width &&
qdev->dumb_heads[index].height == height)
return;
DRM_DEBUG("#%d: %dx%d -> %dx%d\n", index,
qdev->dumb_heads[index].width,
qdev->dumb_heads[index].height,
width, height);
qdev->dumb_heads[index].width = width;
qdev->dumb_heads[index].height = height;
}
static void qxl_calc_dumb_shadow(struct qxl_device *qdev,
struct qxl_surface *surf)
{
struct qxl_head *head;
int i;
memset(surf, 0, sizeof(*surf));
for (i = 0; i < qdev->monitors_config->max_allowed; i++) {
head = qdev->dumb_heads + i;
head->x = surf->width;
surf->width += head->width;
if (surf->height < head->height)
surf->height = head->height;
}
if (surf->width < 64)
surf->width = 64;
if (surf->height < 64)
surf->height = 64;
surf->format = SPICE_SURFACE_FMT_32_xRGB;
surf->stride = surf->width * 4;
if (!qdev->dumb_shadow_bo ||
qdev->dumb_shadow_bo->surf.width != surf->width ||
qdev->dumb_shadow_bo->surf.height != surf->height)
DRM_DEBUG("%dx%d\n", surf->width, surf->height);
}
static int qxl_plane_prepare_fb(struct drm_plane *plane, static int qxl_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state) struct drm_plane_state *new_state)
{ {
struct qxl_device *qdev = plane->dev->dev_private; struct qxl_device *qdev = plane->dev->dev_private;
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct qxl_bo *user_bo, *old_bo = NULL; struct qxl_bo *user_bo;
struct qxl_surface surf;
int ret; int ret;
if (!new_state->fb) if (!new_state->fb)
...@@ -722,29 +786,30 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane, ...@@ -722,29 +786,30 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane,
user_bo = gem_to_qxl_bo(obj); user_bo = gem_to_qxl_bo(obj);
if (plane->type == DRM_PLANE_TYPE_PRIMARY && if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
user_bo->is_dumb && !user_bo->shadow) { user_bo->is_dumb) {
if (plane->state->fb) { qxl_update_dumb_head(qdev, new_state->crtc->index,
obj = plane->state->fb->obj[0]; user_bo);
old_bo = gem_to_qxl_bo(obj); qxl_calc_dumb_shadow(qdev, &surf);
if (!qdev->dumb_shadow_bo ||
qdev->dumb_shadow_bo->surf.width != surf.width ||
qdev->dumb_shadow_bo->surf.height != surf.height) {
if (qdev->dumb_shadow_bo) {
drm_gem_object_put_unlocked
(&qdev->dumb_shadow_bo->gem_base);
qdev->dumb_shadow_bo = NULL;
}
qxl_bo_create(qdev, surf.height * surf.stride,
true, true, QXL_GEM_DOMAIN_SURFACE, &surf,
&qdev->dumb_shadow_bo);
} }
if (old_bo && old_bo->shadow && if (user_bo->shadow != qdev->dumb_shadow_bo) {
user_bo->gem_base.size == old_bo->gem_base.size && if (user_bo->shadow) {
plane->state->crtc == new_state->crtc && drm_gem_object_put_unlocked
plane->state->crtc_w == new_state->crtc_w && (&user_bo->shadow->gem_base);
plane->state->crtc_h == new_state->crtc_h && user_bo->shadow = NULL;
plane->state->src_x == new_state->src_x && }
plane->state->src_y == new_state->src_y && drm_gem_object_get(&qdev->dumb_shadow_bo->gem_base);
plane->state->src_w == new_state->src_w && user_bo->shadow = qdev->dumb_shadow_bo;
plane->state->src_h == new_state->src_h &&
plane->state->rotation == new_state->rotation &&
plane->state->zpos == new_state->zpos) {
drm_gem_object_get(&old_bo->shadow->gem_base);
user_bo->shadow = old_bo->shadow;
} else {
qxl_bo_create(qdev, user_bo->gem_base.size,
true, true, QXL_GEM_DOMAIN_SURFACE, NULL,
&user_bo->shadow);
user_bo->shadow->surf = user_bo->surf;
} }
} }
...@@ -773,7 +838,7 @@ static void qxl_plane_cleanup_fb(struct drm_plane *plane, ...@@ -773,7 +838,7 @@ static void qxl_plane_cleanup_fb(struct drm_plane *plane,
user_bo = gem_to_qxl_bo(obj); user_bo = gem_to_qxl_bo(obj);
qxl_bo_unpin(user_bo); qxl_bo_unpin(user_bo);
if (user_bo->shadow && !user_bo->shadow->is_primary) { if (old_state->fb != plane->state->fb && user_bo->shadow) {
drm_gem_object_put_unlocked(&user_bo->shadow->gem_base); drm_gem_object_put_unlocked(&user_bo->shadow->gem_base);
user_bo->shadow = NULL; user_bo->shadow = NULL;
} }
...@@ -1106,6 +1171,12 @@ int qxl_create_monitors_object(struct qxl_device *qdev) ...@@ -1106,6 +1171,12 @@ int qxl_create_monitors_object(struct qxl_device *qdev)
memset(qdev->monitors_config, 0, monitors_config_size); memset(qdev->monitors_config, 0, monitors_config_size);
qdev->monitors_config->max_allowed = max_allowed; qdev->monitors_config->max_allowed = max_allowed;
qdev->dumb_heads = kcalloc(max_allowed, sizeof(qdev->dumb_heads[0]), GFP_KERNEL);
if (!qdev->dumb_heads) {
qxl_destroy_monitors_object(qdev);
return -ENOMEM;
}
return 0; return 0;
} }
......
...@@ -267,7 +267,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, ...@@ -267,7 +267,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
struct qxl_bo *bo, struct qxl_bo *bo,
unsigned int flags, unsigned int color, unsigned int flags, unsigned int color,
struct drm_clip_rect *clips, struct drm_clip_rect *clips,
unsigned int num_clips, int inc) unsigned int num_clips, int inc,
uint32_t dumb_shadow_offset)
{ {
/* /*
* TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should * TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should
...@@ -295,6 +296,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, ...@@ -295,6 +296,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
if (ret) if (ret)
return; return;
clips->x1 += dumb_shadow_offset;
clips->x2 += dumb_shadow_offset;
left = clips->x1; left = clips->x1;
right = clips->x2; right = clips->x2;
top = clips->y1; top = clips->y1;
...@@ -342,7 +346,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, ...@@ -342,7 +346,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
goto out_release_backoff; goto out_release_backoff;
ret = qxl_image_init(qdev, release, dimage, surface_base, ret = qxl_image_init(qdev, release, dimage, surface_base,
left, top, width, height, depth, stride); left - dumb_shadow_offset,
top, width, height, depth, stride);
qxl_bo_kunmap(bo); qxl_bo_kunmap(bo);
if (ret) if (ret)
goto out_release_backoff; goto out_release_backoff;
......
...@@ -230,6 +230,8 @@ struct qxl_device { ...@@ -230,6 +230,8 @@ struct qxl_device {
struct qxl_ram_header *ram_header; struct qxl_ram_header *ram_header;
struct qxl_bo *primary_bo; struct qxl_bo *primary_bo;
struct qxl_bo *dumb_shadow_bo;
struct qxl_head *dumb_heads;
struct qxl_memslot main_slot; struct qxl_memslot main_slot;
struct qxl_memslot surfaces_slot; struct qxl_memslot surfaces_slot;
...@@ -437,7 +439,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, ...@@ -437,7 +439,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
struct qxl_bo *bo, struct qxl_bo *bo,
unsigned int flags, unsigned int color, unsigned int flags, unsigned int color,
struct drm_clip_rect *clips, struct drm_clip_rect *clips,
unsigned int num_clips, int inc); unsigned int num_clips, int inc,
uint32_t dumb_shadow_offset);
void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec); void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment