Newer
Older
// SPDX-License-Identifier: GPL-2.0 OR MIT
/******************************************************************************
*
* COPYRIGHT (C) 2014-2023 VMware, Inc., Palo Alto, CA., USA
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
******************************************************************************/
#include "vmwgfx_bo.h"
#include "vmwgfx_kms.h"
#include "vmw_surface_cache.h"
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
#define vmw_crtc_to_stdu(x) \
container_of(x, struct vmw_screen_target_display_unit, base.crtc)
#define vmw_encoder_to_stdu(x) \
container_of(x, struct vmw_screen_target_display_unit, base.encoder)
#define vmw_connector_to_stdu(x) \
container_of(x, struct vmw_screen_target_display_unit, base.connector)
enum stdu_content_type {
SAME_AS_DISPLAY = 0,
SEPARATE_SURFACE,
/**
* struct vmw_stdu_dirty - closure structure for the update functions
*
* @base: The base type we derive from. Used by vmw_kms_helper_dirty().
* @left: Left side of bounding box.
* @right: Right side of bounding box.
* @top: Top side of bounding box.
* @bottom: Bottom side of bounding box.
* @fb_left: Left side of the framebuffer/content bounding box
* @fb_top: Top of the framebuffer/content bounding box
* @pitch: framebuffer pitch (stride)
* @buf: buffer object when DMA-ing between buffer and screen targets.
* @sid: Surface ID when copying between surface and screen targets.
*/
struct vmw_stdu_dirty {
struct vmw_kms_dirty base;
s32 left, right, top, bottom;
u32 pitch;
union {
u32 sid;
};
};
/*
* SVGA commands that are used by this code. Please see the device headers
* for explanation.
*/
struct vmw_stdu_update {
SVGA3dCmdHeader header;
SVGA3dCmdUpdateGBScreenTarget body;
};
struct vmw_stdu_dma {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceDMA body;
};
struct vmw_stdu_surface_copy {
SVGA3dCmdHeader header;
SVGA3dCmdSurfaceCopy body;
};
struct vmw_stdu_update_gb_image {
SVGA3dCmdHeader header;
SVGA3dCmdUpdateGBImage body;
};
* struct vmw_screen_target_display_unit - conglomerated STDU structure
*
* @base: VMW specific DU structure
* @display_srf: surface to be displayed. The dimension of this will always
* match the display mode. If the display mode matches
* content_vfbs dimensions, then this is a pointer into the
* corresponding field in content_vfbs. If not, then this
* is a separate buffer to which content_vfbs will blit to.
* @content_fb_type: content_fb type
* @display_width: display width
* @display_height: display height
* @defined: true if the current display unit has been initialized
* @cpp: Bytes per pixel
*/
struct vmw_screen_target_display_unit {
struct vmw_display_unit base;
struct vmw_surface *display_srf;
enum stdu_content_type content_fb_type;
s32 display_width, display_height;
/* For CPU Blit */
unsigned int cpp;
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
};
static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu);
/******************************************************************************
* Screen Target Display Unit CRTC Functions
*****************************************************************************/
/**
* vmw_stdu_crtc_destroy - cleans up the STDU
*
* @crtc: used to get a reference to the containing STDU
*/
static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc)
{
vmw_stdu_destroy(vmw_crtc_to_stdu(crtc));
}
/**
* vmw_stdu_define_st - Defines a Screen Target
*
* @dev_priv: VMW DRM device
* @stdu: display unit to create a Screen Target for
* @mode: The mode to set.
* @crtc_x: X coordinate of screen target relative to framebuffer origin.
* @crtc_y: Y coordinate of screen target relative to framebuffer origin.
*
* Creates a STDU that we can used later. This function is called whenever the
* framebuffer size changes.
*
* RETURNs:
* 0 on success, error code on failure
*/
static int vmw_stdu_define_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu,
struct drm_display_mode *mode,
int crtc_x, int crtc_y)
{
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDefineGBScreenTarget body;
} *cmd;
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL))
return -ENOMEM;
cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SCREENTARGET;
cmd->header.size = sizeof(cmd->body);
cmd->body.stid = stdu->base.unit;
cmd->body.width = mode->hdisplay;
cmd->body.height = mode->vdisplay;
cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0;
cmd->body.dpi = 0;
cmd->body.xRoot = crtc_x;
cmd->body.yRoot = crtc_y;
stdu->base.set_gui_x = cmd->body.xRoot;
stdu->base.set_gui_y = cmd->body.yRoot;
vmw_cmd_commit(dev_priv, sizeof(*cmd));
stdu->display_width = mode->hdisplay;
stdu->display_height = mode->vdisplay;
return 0;
}
/**
* vmw_stdu_bind_st - Binds a surface to a Screen Target
*
* @dev_priv: VMW DRM device
* @stdu: display unit affected
* @res: Buffer to bind to the screen target. Set to NULL to blank screen.
*
* Binding a surface to a Screen Target the same as flipping
*
* Returns: %0 on success or -errno code on failure
*/
static int vmw_stdu_bind_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu,
const struct vmw_resource *res)
{
SVGA3dSurfaceImageId image;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdBindGBScreenTarget body;
} *cmd;
if (!stdu->defined) {
DRM_ERROR("No screen target defined\n");
return -EINVAL;
}
/* Set up image using information in vfb */
memset(&image, 0, sizeof(image));
image.sid = res ? res->id : SVGA3D_INVALID_ID;
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL))
return -ENOMEM;
cmd->header.id = SVGA_3D_CMD_BIND_GB_SCREENTARGET;
cmd->header.size = sizeof(cmd->body);
cmd->body.stid = stdu->base.unit;
cmd->body.image = image;
vmw_cmd_commit(dev_priv, sizeof(*cmd));
/**
* vmw_stdu_populate_update - populate an UPDATE_GB_SCREENTARGET command with a
* bounding box.
*
* @cmd: Pointer to command stream.
* @unit: Screen target unit.
* @left: Left side of bounding box.
* @right: Right side of bounding box.
* @top: Top side of bounding box.
* @bottom: Bottom side of bounding box.
*/
static void vmw_stdu_populate_update(void *cmd, int unit,
s32 left, s32 right, s32 top, s32 bottom)
{
struct vmw_stdu_update *update = cmd;
update->header.id = SVGA_3D_CMD_UPDATE_GB_SCREENTARGET;
update->header.size = sizeof(update->body);
update->body.stid = unit;
update->body.rect.x = left;
update->body.rect.y = top;
update->body.rect.w = right - left;
update->body.rect.h = bottom - top;
}
* vmw_stdu_update_st - Full update of a Screen Target
*
* @dev_priv: VMW DRM device
* @stdu: display unit affected
*
* This function needs to be called whenever the content of a screen
* target has changed completely. Typically as a result of a backing
* surface change.
*
* RETURNS:
* 0 on success, error code on failure
*/
static int vmw_stdu_update_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu)
struct vmw_stdu_update *cmd;
if (!stdu->defined) {
DRM_ERROR("No screen target defined");
return -EINVAL;
}
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL))
vmw_stdu_populate_update(cmd, stdu->base.unit,
0, stdu->display_width,
0, stdu->display_height);
vmw_cmd_commit(dev_priv, sizeof(*cmd));
return 0;
}
/**
* vmw_stdu_destroy_st - Destroy a Screen Target
*
* @dev_priv: VMW DRM device
* @stdu: display unit to destroy
*
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
static int vmw_stdu_destroy_st(struct vmw_private *dev_priv,
struct vmw_screen_target_display_unit *stdu)
{
int ret;
struct {
SVGA3dCmdHeader header;
SVGA3dCmdDestroyGBScreenTarget body;
} *cmd;
/* Nothing to do if not successfully defined */
if (unlikely(!stdu->defined))
return 0;
cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));
if (unlikely(cmd == NULL))
return -ENOMEM;
cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SCREENTARGET;
cmd->header.size = sizeof(cmd->body);
cmd->body.stid = stdu->base.unit;
vmw_cmd_commit(dev_priv, sizeof(*cmd));
/* Force sync */
ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ);
if (unlikely(ret != 0))
DRM_ERROR("Failed to sync with HW");
stdu->defined = false;
stdu->display_width = 0;
stdu->display_height = 0;
/**
* vmw_stdu_crtc_mode_set_nofb - Updates screen target size
*
* @crtc: CRTC associated with the screen target
*
* This function defines/destroys a screen target
*
*/
static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
struct vmw_private *dev_priv;
struct vmw_screen_target_display_unit *stdu;
struct drm_connector_state *conn_state;
struct vmw_connector_state *vmw_conn_state;
int x, y, ret;
stdu = vmw_crtc_to_stdu(crtc);
dev_priv = vmw_priv(crtc->dev);
conn_state = stdu->base.connector.state;
vmw_conn_state = vmw_connector_state_to_vcs(conn_state);
if (stdu->defined) {
ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
if (ret)
DRM_ERROR("Failed to blank CRTC\n");
(void) vmw_stdu_update_st(dev_priv, stdu);
ret = vmw_stdu_destroy_st(dev_priv, stdu);
if (ret)
DRM_ERROR("Failed to destroy Screen Target\n");
stdu->content_fb_type = SAME_AS_DISPLAY;
}
if (!crtc->state->enable)
return;
x = vmw_conn_state->gui_x;
y = vmw_conn_state->gui_y;
vmw_svga_enable(dev_priv);
ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, x, y);
if (ret)
DRM_ERROR("Failed to define Screen Target of size %dx%d\n",
crtc->x, crtc->y);
}
static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc)
{
}
static void vmw_stdu_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
{
struct vmw_private *dev_priv;
struct vmw_screen_target_display_unit *stdu;
int ret;
if (!crtc) {
DRM_ERROR("CRTC is NULL\n");
return;
}
stdu = vmw_crtc_to_stdu(crtc);
dev_priv = vmw_priv(crtc->dev);
if (stdu->defined) {
ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
if (ret)
DRM_ERROR("Failed to blank CRTC\n");
(void) vmw_stdu_update_st(dev_priv, stdu);
ret = vmw_stdu_destroy_st(dev_priv, stdu);
if (ret)
DRM_ERROR("Failed to destroy Screen Target\n");
stdu->content_fb_type = SAME_AS_DISPLAY;
}
}
* vmw_stdu_bo_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_bo_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 destination 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);
/*
* Calculate content bounding box. We only need the top-left
* coordinate because width and height will be the same as the
* destination bounding box above
*/
ddirty->fb_left = min_t(s32, ddirty->fb_left, dirty->fb_x);
ddirty->fb_top = min_t(s32, ddirty->fb_top, dirty->fb_y);
* vmw_stdu_bo_cpu_commit - Callback to do a CPU blit from buffer object
*
* @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_bo_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;

Thomas Hellstrom
committed
struct ttm_buffer_object *src_bo, *dst_bo;
u32 src_offset, dst_offset;
struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(stdu->cpp);
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 Guest (bo) to Host (display_srf) */
src_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp;
src_bo = &stdu->display_srf->res.guest_memory_bo->tbo;
src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp;
dst_offset = ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp;

Thomas Hellstrom
committed
(void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch,
src_bo, src_offset, src_pitch,
width * stdu->cpp, height, &diff);
* vmw_kms_stdu_readback - Perform a readback from a buffer-object backed
* framebuffer and the screen target system.
*
* @dev_priv: Pointer to the device private structure.
* @file_priv: Pointer to a struct drm-file identifying the caller. May be
* set to NULL, but then @user_fence_rep must also be set to NULL.
* @vfb: Pointer to the buffer-object backed framebuffer.
* @user_fence_rep: User-space provided structure for fence information.
* @clips: Array of clip rects. Either @clips or @vclips must be NULL.
* @vclips: Alternate array of clip rects. Either @clips or @vclips must
* be NULL.
* @num_clips: Number of clip rects in @clips or @vclips.
* @increment: Increment to use when looping over @clips or @vclips.
* @crtc: If crtc is passed, perform stdu dma on that crtc only.
*
* If DMA-ing till the screen target system, the function will also notify
* the screen target system that a bounding box of the cliprects has been
* updated.
*
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
int vmw_kms_stdu_readback(struct vmw_private *dev_priv,
struct drm_file *file_priv,
struct vmw_framebuffer *vfb,
struct drm_vmw_fence_rep __user *user_fence_rep,
struct drm_clip_rect *clips,
struct drm_vmw_rect *vclips,
uint32_t num_clips,
int increment,
struct drm_crtc *crtc)
container_of(vfb, struct vmw_framebuffer_bo, base)->buffer;
struct vmw_stdu_dirty ddirty;
int ret;
DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);

Thomas Hellstrom
committed
/*
* The GMR domain might seem confusing because it might seem like it should
* never happen with screen targets but e.g. the xorg vmware driver issues
* CMD_SURFACE_DMA for various pixmap updates which might transition our bo to
* a GMR. Instead of forcing another transition we can optimize the readback
* by reading directly from the GMR.

Thomas Hellstrom
committed
*/
vmw_bo_placement_set(buf,
VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR,
VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR);
ret = vmw_validation_add_bo(&val_ctx, buf);
if (ret)
return ret;
ret = vmw_validation_prepare(&val_ctx, NULL, true);
if (ret)
goto out_unref;
ddirty.left = ddirty.top = S32_MAX;
ddirty.right = ddirty.bottom = S32_MIN;
ddirty.fb_left = ddirty.fb_top = S32_MAX;
ddirty.pitch = vfb->base.pitches[0];
ddirty.buf = buf;
ddirty.base.fifo_commit = vmw_stdu_bo_cpu_commit;
ddirty.base.clip = vmw_stdu_bo_cpu_clip;
ddirty.base.fifo_reserve_size = 0;
ddirty.base.crtc = crtc;
ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips,
0, 0, num_clips, increment, &ddirty.base);
vmw_kms_helper_validation_finish(dev_priv, file_priv, &val_ctx, NULL,
user_fence_rep);
return ret;
out_unref:
vmw_validation_unref_lists(&val_ctx);
return ret;
}
/**
* vmw_kms_stdu_surface_clip - Callback to encode a surface copy command cliprect
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
*
* @dirty: The closure structure.
*
* Encodes a surface copy command cliprect and updates the bounding box
* for the copy.
*/
static void vmw_kms_stdu_surface_clip(struct vmw_kms_dirty *dirty)
{
struct vmw_stdu_dirty *sdirty =
container_of(dirty, struct vmw_stdu_dirty, base);
struct vmw_stdu_surface_copy *cmd = dirty->cmd;
struct vmw_screen_target_display_unit *stdu =
container_of(dirty->unit, typeof(*stdu), base);
if (sdirty->sid != stdu->display_srf->res.id) {
struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
blit += dirty->num_hits;
blit->srcx = dirty->fb_x;
blit->srcy = dirty->fb_y;
blit->x = dirty->unit_x1;
blit->y = dirty->unit_y1;
blit->d = 1;
blit->w = dirty->unit_x2 - dirty->unit_x1;
blit->h = dirty->unit_y2 - dirty->unit_y1;
}
dirty->num_hits++;
/* Destination bounding box */
sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1);
sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1);
sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2);
sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2);
}
/**
* vmw_kms_stdu_surface_fifo_commit - Callback to fill in and submit a surface
* copy command.
*
* @dirty: The closure structure.
*
* Fills in the missing fields in a surface copy command, and encodes a screen
* target update command.
*/
static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty)
{
struct vmw_stdu_dirty *sdirty =
container_of(dirty, struct vmw_stdu_dirty, base);
struct vmw_screen_target_display_unit *stdu =
container_of(dirty->unit, typeof(*stdu), base);
struct vmw_stdu_surface_copy *cmd = dirty->cmd;
struct vmw_stdu_update *update;
size_t blit_size = sizeof(SVGA3dCopyBox) * dirty->num_hits;
size_t commit_size;
if (!dirty->num_hits) {
return;
}
if (sdirty->sid != stdu->display_srf->res.id) {
struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
cmd->header.id = SVGA_3D_CMD_SURFACE_COPY;
cmd->header.size = sizeof(cmd->body) + blit_size;
cmd->body.src.sid = sdirty->sid;
cmd->body.dest.sid = stdu->display_srf->res.id;
update = (struct vmw_stdu_update *) &blit[dirty->num_hits];
commit_size = sizeof(*cmd) + blit_size + sizeof(*update);
stdu->display_srf->res.res_dirty = true;
} else {
update = dirty->cmd;
commit_size = sizeof(*update);
}
vmw_stdu_populate_update(update, stdu->base.unit, sdirty->left,
sdirty->right, sdirty->top, sdirty->bottom);
vmw_cmd_commit(dirty->dev_priv, commit_size);
sdirty->left = sdirty->top = S32_MAX;
sdirty->right = sdirty->bottom = S32_MIN;
}
/**
* vmw_kms_stdu_surface_dirty - Dirty part of a surface backed framebuffer
*
* @dev_priv: Pointer to the device private structure.
* @framebuffer: Pointer to the surface-buffer backed framebuffer.
* @clips: Array of clip rects. Either @clips or @vclips must be NULL.
* @vclips: Alternate array of clip rects. Either @clips or @vclips must
* be NULL.
* @srf: Pointer to surface to blit from. If NULL, the surface attached
* to @framebuffer will be used.
* @dest_x: X coordinate offset to align @srf with framebuffer coordinates.
* @dest_y: Y coordinate offset to align @srf with framebuffer coordinates.
* @num_clips: Number of clip rects in @clips.
* @inc: Increment to use when looping over @clips.
* @out_fence: If non-NULL, will return a ref-counted pointer to a
* struct vmw_fence_obj. The returned fence pointer may be NULL in which
* case the device has already synchronized.
* @crtc: If crtc is passed, perform surface dirty on that crtc only.
* Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
* interrupted.
*/
int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
struct vmw_framebuffer *framebuffer,
struct drm_clip_rect *clips,
struct drm_vmw_rect *vclips,
struct vmw_resource *srf,
s32 dest_x,
s32 dest_y,
unsigned num_clips, int inc,
struct vmw_fence_obj **out_fence,
struct drm_crtc *crtc)
{
struct vmw_framebuffer_surface *vfbs =
container_of(framebuffer, typeof(*vfbs), base);
struct vmw_stdu_dirty sdirty;
DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);
int ret;
if (!srf)
srf = &vfbs->surface->res;
ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE,
NULL, NULL);
if (ret)
return ret;
ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true);
if (ret)
goto out_unref;
if (vfbs->is_bo_proxy) {
ret = vmw_kms_update_proxy(srf, clips, num_clips, inc);
if (ret)
goto out_finish;
}
sdirty.base.fifo_commit = vmw_kms_stdu_surface_fifo_commit;
sdirty.base.clip = vmw_kms_stdu_surface_clip;
sdirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_surface_copy) +
sizeof(SVGA3dCopyBox) * num_clips +
sizeof(struct vmw_stdu_update);
sdirty.base.crtc = crtc;
sdirty.sid = srf->id;
sdirty.left = sdirty.top = S32_MAX;
sdirty.right = sdirty.bottom = S32_MIN;
ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,
dest_x, dest_y, num_clips, inc,
&sdirty.base);
out_finish:
vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence,
NULL);
return ret;
out_unref:
vmw_validation_unref_lists(&val_ctx);
return ret;
}
/*
* Screen Target CRTC dispatch table
*/
static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_stdu_crtc_destroy,
.reset = vmw_du_crtc_reset,
.atomic_duplicate_state = vmw_du_crtc_duplicate_state,
.atomic_destroy_state = vmw_du_crtc_destroy_state,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
};
/******************************************************************************
* Screen Target Display Unit Encoder Functions
*****************************************************************************/
/**
* vmw_stdu_encoder_destroy - cleans up the STDU
*
* @encoder: used the get the containing STDU
*
* vmwgfx cleans up crtc/encoder/connector all at the same time so technically
* this can be a no-op. Nevertheless, it doesn't hurt of have this in case
* the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't
* get called.
*/
static void vmw_stdu_encoder_destroy(struct drm_encoder *encoder)
{
vmw_stdu_destroy(vmw_encoder_to_stdu(encoder));
}
static const struct drm_encoder_funcs vmw_stdu_encoder_funcs = {
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
.destroy = vmw_stdu_encoder_destroy,
};
/******************************************************************************
* Screen Target Display Unit Connector Functions
*****************************************************************************/
/**
* vmw_stdu_connector_destroy - cleans up the STDU
*
* @connector: used to get the containing STDU
*
* vmwgfx cleans up crtc/encoder/connector all at the same time so technically
* this can be a no-op. Nevertheless, it doesn't hurt of have this in case
* the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't
* get called.
*/
static void vmw_stdu_connector_destroy(struct drm_connector *connector)
{
vmw_stdu_destroy(vmw_connector_to_stdu(connector));
}
static const struct drm_connector_funcs vmw_stdu_connector_funcs = {
.dpms = vmw_du_connector_dpms,
.detect = vmw_du_connector_detect,
.fill_modes = vmw_du_connector_fill_modes,
.destroy = vmw_stdu_connector_destroy,
.reset = vmw_du_connector_reset,
.atomic_duplicate_state = vmw_du_connector_duplicate_state,
.atomic_destroy_state = vmw_du_connector_destroy_state,
static const struct
drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = {
};
/******************************************************************************
* Screen Target Display Plane Functions
*****************************************************************************/
/**
* vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface
*
* @plane: display plane
* @old_state: Contains the FB to clean up
*
* Unpins the display surface
*
* Returns 0 on success
*/
static void
vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
if (vps->surf)
WARN_ON(!vps->pinned);
vmw_du_plane_cleanup_fb(plane, old_state);
vps->content_fb_type = SAME_AS_DISPLAY;
}
/**
* vmw_stdu_primary_plane_prepare_fb - Readies the display surface
*
* @plane: display plane
* @new_state: info on the new plane state, including the FB
*
* This function allocates a new display surface if the content is
* backed by a buffer object. The display surface is pinned here, and it'll
* be unpinned in .cleanup_fb()
*
*/
static int
vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
struct vmw_private *dev_priv = vmw_priv(plane->dev);
struct drm_framebuffer *new_fb = new_state->fb;
struct vmw_framebuffer *vfb;
struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
enum stdu_content_type new_content_type;
struct vmw_framebuffer_surface *new_vfbs;
uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;
int ret;
/* No FB to prepare */
if (!new_fb) {
if (vps->surf) {
WARN_ON(vps->pinned != 0);
vmw_surface_unreference(&vps->surf);
}
return 0;
}
vfb = vmw_framebuffer_to_vfb(new_fb);
new_vfbs = (vfb->bo) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
if (new_vfbs &&
new_vfbs->surface->metadata.base_size.width == hdisplay &&
new_vfbs->surface->metadata.base_size.height == vdisplay)
new_content_type = SAME_AS_DISPLAY;
else if (vfb->bo)
new_content_type = SEPARATE_BO;
else
new_content_type = SEPARATE_SURFACE;
if (new_content_type != SAME_AS_DISPLAY) {
struct vmw_surface_metadata metadata = {0};
* If content buffer is a buffer object, then we have to
* construct surface info
if (new_content_type == SEPARATE_BO) {
switch (new_fb->format->cpp[0]*8) {
case 32:
metadata.format = SVGA3D_X8R8G8B8;
break;
case 16:
metadata.format = SVGA3D_R5G6B5;
break;
case 8:
metadata.format = SVGA3D_P8;
break;
default:
DRM_ERROR("Invalid format\n");
return -EINVAL;
}
metadata.mip_levels[0] = 1;
metadata.num_sizes = 1;
metadata.scanout = true;
metadata = new_vfbs->surface->metadata;
metadata.base_size.width = hdisplay;
metadata.base_size.height = vdisplay;
metadata.base_size.depth = 1;
struct drm_vmw_size cur_base_size =
vps->surf->metadata.base_size;
if (cur_base_size.width != metadata.base_size.width ||
cur_base_size.height != metadata.base_size.height ||
vps->surf->metadata.format != metadata.format) {
WARN_ON(vps->pinned != 0);
vmw_surface_unreference(&vps->surf);
}
}
if (!vps->surf) {
ret = vmw_gb_surface_define(dev_priv, &metadata,
&vps->surf);
if (ret != 0) {
DRM_ERROR("Couldn't allocate STDU surface.\n");
return ret;
}
}
} else {
/*
* prepare_fb and clean_fb should only take care of pinning
* and unpinning. References are tracked by state objects.
* The only time we add a reference in prepare_fb is if the
* state object doesn't have a reference to begin with
*/
if (vps->surf) {
WARN_ON(vps->pinned != 0);
vmw_surface_unreference(&vps->surf);