Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE
drm.7039
1011-drm-vmwgfx-Support-topology-greater-than-t...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 1011-drm-vmwgfx-Support-topology-greater-than-texture-siz.patch of Package drm.7039
From c8393fd645e7c3e7de5fbadaee04527468e91024 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh <syeh@vmware.com> Date: Fri, 14 Jul 2017 00:10:32 -0700 Subject: [PATCH 1/3] drm/vmwgfx: Support topology greater than texture size References: bsc#1048155 Patch-mainline: 4.12-rc1 Git-commit 810b3e1683d00e51c8392bcee7dc7a1c65354777 Most of the display servers today use a single surface to represent the entire desktop even if it's stretched across multiple screens. For vmwgfx with STDU, the maximum surface size is limited to the maximum texture size on the host. On a 2D VM, this limits our ability to support configurations with more than one 4K monitor. To get past this limitation, we will now allow using a large DMA buf as the framebuffer, and take care of blitting contents from this DMA buf to the display buffer. backported Commit 810b3e1683d0 upstream. Signed-off-by: Sinclair Yeh <syeh@vmware.com> Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com> --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 30 ++++- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 209 ++++++++++++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 2 deletions(-) --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -900,6 +900,24 @@ out_err1: } /** + * vmw_kms_srf_ok - check if a surface can be created + * + * @width: requested width + * @height: requested height + * + * Surfaces need to be less than texture size + */ +static bool +vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) +{ + if (width > dev_priv->texture_max_width || + height > dev_priv->texture_max_height) + return false; + + return true; +} + +/** * vmw_kms_new_framebuffer - Create a new framebuffer. * * @dev_priv: Pointer to device private struct. @@ -927,7 +945,8 @@ vmw_kms_new_framebuffer(struct vmw_priva * therefore, wrap the DMA buf in a surface so we can use the * SurfaceCopy command. */ - if (dmabuf && only_2d && + if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && + dmabuf && only_2d && dev_priv->active_display_unit == vmw_du_screen_target) { ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd, dmabuf, &surface); @@ -1028,6 +1047,15 @@ static struct drm_framebuffer *vmw_kms_f if (ret) goto err_out; + + if (!bo && !vmw_kms_srf_ok(dev_priv, mode_cmd.width, mode_cmd.height)) { + DRM_ERROR("Surface size cannot exceed %dx%d", + dev_priv->texture_max_width, + dev_priv->texture_max_height); + goto err_out; + } + + vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, !(dev_priv->capabilities & SVGA_CAP_3D), &mode_cmd); --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -106,6 +106,10 @@ struct vmw_screen_target_display_unit { enum stdu_content_type content_fb_type; bool defined; + + /* For CPU Blit */ + struct ttm_bo_kmap_obj host_map, guest_map; + unsigned int cpp; }; @@ -483,9 +487,50 @@ static int vmw_stdu_bind_fb(struct vmw_p /* Transfer the reference */ stdu->display_srf = new_display_srf; - new_display_srf = NULL; } + /* + * This should only happen if the DMA buf is too large to create a + * proxy surface for. + * If we are a 2D VM with a DMA buffer then we have to use CPU blit + * so cache these mappings + */ + if (new_content_type == SEPARATE_DMA && + !(dev_priv->capabilities & SVGA_CAP_3D)) { + + struct vmw_framebuffer_dmabuf *new_vfbd; + + new_vfbd = vmw_framebuffer_to_vfbd(new_fb); + + ret = ttm_bo_reserve(&new_vfbd->buffer->base, false, false, + NULL); + if (ret) + goto out_srf_unpin; + + ret = ttm_bo_kmap(&new_vfbd->buffer->base, 0, + new_vfbd->buffer->base.num_pages, + &stdu->guest_map); + + ttm_bo_unreserve(&new_vfbd->buffer->base); + + if (ret) { + DRM_ERROR("Failed to map content buffer to CPU\n"); + goto out_srf_unpin; + } + + ret = ttm_bo_kmap(&new_display_srf->res.backup->base, 0, + new_display_srf->res.backup->base.num_pages, + &stdu->host_map); + if (ret) { + DRM_ERROR("Failed to map display buffer to CPU\n"); + ttm_bo_kunmap(&stdu->guest_map); + goto out_srf_unpin; + } + + stdu->cpp = new_fb->bits_per_pixel / 8; + } + + crtc->primary->fb = new_fb; stdu->content_fb_type = new_content_type; return 0; @@ -572,6 +617,12 @@ static int vmw_stdu_crtc_set_config(stru if (ret) return ret; + if (stdu->guest_map.virtual) + ttm_bo_kunmap(&stdu->guest_map); + + if (stdu->host_map.virtual) + ttm_bo_kunmap(&stdu->host_map); + vmw_stdu_unpin_display(stdu); (void) vmw_stdu_update_st(dev_priv, stdu); vmw_kms_del_active(dev_priv, &stdu->base); @@ -791,6 +842,130 @@ static void vmw_stdu_dmabuf_fifo_commit( ddirty->right = ddirty->bottom = S32_MIN; } + +/** + * vmw_stdu_dmabuf_cpu_clip - Callback to encode a CPU blit + * + * @dirty: The closure structure. + * + * This function calculates the bounding box for all the incoming clips + */ +static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + + dirty->num_hits = 1; + + /* Calculate bounding box */ + ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1); + ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1); + ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2); + ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2); +} + + +/** + * vmw_stdu_dmabuf_cpu_commit - Callback to do a CPU blit from DMAbuf + * + * @dirty: The closure structure. + * + * For the special case when we cannot create a proxy surface in a + * 2D VM, we have to do a CPU blit ourselves. + */ +static void vmw_stdu_dmabuf_cpu_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + s32 width, height; + s32 src_pitch, dst_pitch; + u8 *src, *dst; + bool not_used; + + + if (!dirty->num_hits) + return; + + width = ddirty->right - ddirty->left; + height = ddirty->bottom - ddirty->top; + + if (width == 0 || height == 0) + return; + + + /* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */ + src_pitch = stdu->display_srf->base_size.width * stdu->cpp; + src = ttm_kmap_obj_virtual(&stdu->host_map, ¬_used); + src += dirty->unit_y1 * src_pitch + dirty->unit_x1 * stdu->cpp; + + dst_pitch = ddirty->pitch; + dst = ttm_kmap_obj_virtual(&stdu->guest_map, ¬_used); + dst += dirty->fb_y * dst_pitch + dirty->fb_x * stdu->cpp; + + + /* Figure out the real direction */ + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + u8 *tmp; + s32 tmp_pitch; + + tmp = src; + tmp_pitch = src_pitch; + + src = dst; + src_pitch = dst_pitch; + + dst = tmp; + dst_pitch = tmp_pitch; + } + + /* CPU Blit */ + while (height-- > 0) { + memcpy(dst, src, width * stdu->cpp); + dst += dst_pitch; + src += src_pitch; + } + + if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) { + struct vmw_private *dev_priv; + struct vmw_stdu_update *cmd; + struct drm_clip_rect region; + int ret; + + /* We are updating the actual surface, not a proxy */ + region.x1 = ddirty->left; + region.x2 = ddirty->right; + region.y1 = ddirty->top; + region.y2 = ddirty->bottom; + ret = vmw_kms_update_proxy( + (struct vmw_resource *) &stdu->display_srf->res, + (const struct drm_clip_rect *) ®ion, 1, 1); + if (ret) + goto out_cleanup; + + + dev_priv = vmw_priv(stdu->base.crtc.dev); + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + + if (!cmd) { + DRM_ERROR("Cannot reserve FIFO space to update STDU"); + goto out_cleanup; + } + + vmw_stdu_populate_update(cmd, stdu->base.unit, + ddirty->left, ddirty->right, + ddirty->top, ddirty->bottom); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + } + +out_cleanup: + ddirty->left = ddirty->top = S32_MAX; + ddirty->right = ddirty->bottom = S32_MIN; +} + + /** * vmw_kms_stdu_dma - Perform a DMA transfer between a dma-buffer backed * framebuffer and the screen target system. @@ -849,6 +1024,13 @@ int vmw_kms_stdu_dma(struct vmw_private if (to_surface) ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update); + /* 2D VMs cannot use SVGA_3D_CMD_SURFACE_DMA so do CPU blit instead */ + if (!(dev_priv->capabilities & SVGA_CAP_3D)) { + ddirty.base.fifo_commit = vmw_stdu_dmabuf_cpu_commit; + ddirty.base.clip = vmw_stdu_dmabuf_cpu_clip; + ddirty.base.fifo_reserve_size = 0; + } + ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips, 0, 0, num_clips, increment, &ddirty.base); vmw_kms_helper_buffer_finish(dev_priv, file_priv, buf, NULL, @@ -1201,6 +1383,31 @@ int vmw_kms_stdu_init_display(struct vmw dev_priv->active_display_unit = vmw_du_screen_target; + if (dev_priv->capabilities & SVGA_CAP_3D) { + /* + * For 3D VMs, display (scanout) buffer size is the smaller of + * max texture and max STDU + */ + uint32_t max_width, max_height; + + max_width = min(dev_priv->texture_max_width, + dev_priv->stdu_max_width); + max_height = min(dev_priv->texture_max_height, + dev_priv->stdu_max_height); + + dev->mode_config.max_width = max_width; + dev->mode_config.max_height = max_height; + } else { + /* + * Given various display aspect ratios, there's no way to + * estimate these using prim_bb_mem. So just set these to + * something arbitrarily large and we will reject any layout + * that doesn't fit prim_bb_mem later + */ + dev->mode_config.max_width = 16384; + dev->mode_config.max_height = 16384; + } + vmw_kms_create_implicit_placement_property(dev_priv, false); for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor