Newer
Older
// SPDX-License-Identifier: GPL-2.0 OR MIT
/******************************************************************************
*
* COPYRIGHT (C) 2014-2015 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 <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
#include "vmwgfx_kms.h"
#include "device_include/svga3d_surfacedefs.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().
* @transfer: Transfer direction for DMA command.
* @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
* @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;
SVGA3dTransferType transfer;
s32 left, right, top, bottom;
u32 pitch;
union {
struct vmw_buffer_object *buf;
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
*
* @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_type: content_fb type
* @defined: true if the current display unit has been initialized
*/
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;
125
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_FIFO_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_fifo_commit(dev_priv, sizeof(*cmd));
stdu->defined = true;
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
*/
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_FIFO_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_fifo_commit(dev_priv, sizeof(*cmd));
return 0;
}
/**
* 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_FIFO_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);
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
vmw_fifo_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
*/
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_FIFO_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_fifo_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)
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
{
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_clip - Callback to encode a suface DMA command cliprect
*
* @dirty: The closure structure.
*
* Encodes a surface DMA command cliprect and updates the bounding box
* for the DMA.
*/
static void vmw_stdu_bo_clip(struct vmw_kms_dirty *dirty)
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
{
struct vmw_stdu_dirty *ddirty =
container_of(dirty, struct vmw_stdu_dirty, base);
struct vmw_stdu_dma *cmd = dirty->cmd;
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++;
if (ddirty->transfer != SVGA3D_WRITE_HOST_VRAM)
return;
/* 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);
}
/**
* vmw_stdu_bo_fifo_commit - Callback to fill in and submit a DMA command.
*
* @dirty: The closure structure.
*
* Fills in the missing fields in a DMA command, and optionally encodes
* a screen target update command, depending on transfer direction.
*/
static void vmw_stdu_bo_fifo_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);
struct vmw_stdu_dma *cmd = dirty->cmd;
struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
SVGA3dCmdSurfaceDMASuffix *suffix =
(SVGA3dCmdSurfaceDMASuffix *) &blit[dirty->num_hits];
size_t blit_size = sizeof(*blit) * dirty->num_hits + sizeof(*suffix);
if (!dirty->num_hits) {
vmw_fifo_commit(dirty->dev_priv, 0);
return;
}
cmd->header.id = SVGA_3D_CMD_SURFACE_DMA;
cmd->header.size = sizeof(cmd->body) + blit_size;
vmw_bo_get_guest_ptr(&ddirty->buf->base, &cmd->body.guest.ptr);
cmd->body.guest.pitch = ddirty->pitch;
cmd->body.host.sid = stdu->display_srf->res.id;
cmd->body.host.face = 0;
cmd->body.host.mipmap = 0;
cmd->body.transfer = ddirty->transfer;
suffix->suffixSize = sizeof(*suffix);
suffix->maximumOffset = ddirty->buf->base.base.size;
if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) {
blit_size += sizeof(struct vmw_stdu_update);
vmw_stdu_populate_update(&suffix[1], stdu->base.unit,
ddirty->left, ddirty->right,
ddirty->top, ddirty->bottom);
}
vmw_fifo_commit(dirty->dev_priv, sizeof(*cmd) + blit_size);
stdu->display_srf->res.res_dirty = true;
ddirty->left = ddirty->top = S32_MAX;
ddirty->right = ddirty->bottom = S32_MIN;
}
* 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) */
dst_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp;

Thomas Hellstrom
committed
dst_bo = &stdu->display_srf->res.backup->base;
dst_offset = ddirty->top * dst_pitch + ddirty->left * stdu->cpp;

Thomas Hellstrom
committed
src_pitch = ddirty->pitch;
src_bo = &ddirty->buf->base;
src_offset = ddirty->fb_top * src_pitch + ddirty->fb_left * stdu->cpp;

Thomas Hellstrom
committed
/* Swap src and dst if the assumption was wrong. */
if (ddirty->transfer != SVGA3D_WRITE_HOST_VRAM) {
swap(dst_pitch, src_pitch);
swap(dst_bo, src_bo);
swap(src_offset, dst_offset);

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);

Thomas Hellstrom
committed
if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM &&
drm_rect_visible(&diff.rect)) {
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 */

Thomas Hellstrom
committed
region.x1 = diff.rect.x1;
region.x2 = diff.rect.x2;
region.y1 = diff.rect.y1;
region.y2 = diff.rect.y2;
ret = vmw_kms_update_proxy(&stdu->display_srf->res, ®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)
goto out_cleanup;
vmw_stdu_populate_update(cmd, stdu->base.unit,

Thomas Hellstrom
committed
region.x1, region.x2,
region.y1, region.y2);
vmw_fifo_commit(dev_priv, sizeof(*cmd));
}
out_cleanup:
ddirty->left = ddirty->top = ddirty->fb_left = ddirty->fb_top = S32_MAX;
ddirty->right = ddirty->bottom = S32_MIN;
}
* vmw_kms_stdu_dma - Perform a DMA transfer between 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.
* @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.
* @to_surface: Whether to DMA to the screen target system as opposed to
* from the screen target system.
* @interruptible: Whether to perform waits interruptible if possible.
* @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_dma(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,
bool to_surface,
bool interruptible,
struct drm_crtc *crtc)
struct vmw_buffer_object *buf =
container_of(vfb, struct vmw_framebuffer_bo, base)->buffer;
struct vmw_stdu_dirty ddirty;
int ret;

Thomas Hellstrom
committed
bool cpu_blit = !(dev_priv->capabilities & SVGA_CAP_3D);
DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);

Thomas Hellstrom
committed
/*
* VMs without 3D support don't have the surface DMA command and
* we'll be using a CPU blit, and the framebuffer should be moved out
* of VRAM.
*/
ret = vmw_validation_add_bo(&val_ctx, buf, false, cpu_blit);
if (ret)
return ret;
ret = vmw_validation_prepare(&val_ctx, NULL, interruptible);
if (ret)
goto out_unref;
ddirty.transfer = (to_surface) ? SVGA3D_WRITE_HOST_VRAM :
SVGA3D_READ_HOST_VRAM;
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_fifo_commit;
ddirty.base.clip = vmw_stdu_bo_clip;
ddirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_dma) +
num_clips * sizeof(SVGA3dCopyBox) +
sizeof(SVGA3dCmdSurfaceDMASuffix);
if (to_surface)
ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update);

Thomas Hellstrom
committed
if (cpu_blit) {
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);
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
return ret;
}
/**
* vmw_stdu_surface_clip - Callback to encode a surface copy command cliprect
*
* @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_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) {
vmw_fifo_commit(dirty->dev_priv, 0);
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;
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
} 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_fifo_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,
.get_vblank_counter = vmw_get_vblank_counter,
.enable_vblank = vmw_enable_vblank,
.disable_vblank = vmw_disable_vblank,
};
/******************************************************************************
* 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 = {
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
.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
*