1714 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2/******************************************************************************3 *4 * Copyright (c) 2014-2024 Broadcom. All Rights Reserved. The term5 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.6 *7 * Permission is hereby granted, free of charge, to any person obtaining a8 * copy of this software and associated documentation files (the9 * "Software"), to deal in the Software without restriction, including10 * without limitation the rights to use, copy, modify, merge, publish,11 * distribute, sub license, and/or sell copies of the Software, and to12 * permit persons to whom the Software is furnished to do so, subject to13 * the following conditions:14 *15 * The above copyright notice and this permission notice (including the16 * next paragraph) shall be included in all copies or substantial portions17 * of the Software.18 *19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE25 * USE OR OTHER DEALINGS IN THE SOFTWARE.26 *27 ******************************************************************************/28 29#include "vmwgfx_bo.h"30#include "vmwgfx_kms.h"31#include "vmwgfx_vkms.h"32#include "vmw_surface_cache.h"33#include <linux/fsnotify.h>34 35#include <drm/drm_atomic.h>36#include <drm/drm_atomic_helper.h>37#include <drm/drm_damage_helper.h>38#include <drm/drm_fourcc.h>39#include <drm/drm_vblank.h>40 41#define vmw_crtc_to_stdu(x) \42 container_of(x, struct vmw_screen_target_display_unit, base.crtc)43#define vmw_encoder_to_stdu(x) \44 container_of(x, struct vmw_screen_target_display_unit, base.encoder)45#define vmw_connector_to_stdu(x) \46 container_of(x, struct vmw_screen_target_display_unit, base.connector)47 48/*49 * Some renderers such as llvmpipe will align the width and height of their50 * buffers to match their tile size. We need to keep this in mind when exposing51 * modes to userspace so that this possible over-allocation will not exceed52 * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the53 * tile size of current renderers.54 */55#define GPU_TILE_SIZE 6456 57enum stdu_content_type {58 SAME_AS_DISPLAY = 0,59 SEPARATE_SURFACE,60 SEPARATE_BO61};62 63/**64 * struct vmw_stdu_dirty - closure structure for the update functions65 *66 * @base: The base type we derive from. Used by vmw_kms_helper_dirty().67 * @left: Left side of bounding box.68 * @right: Right side of bounding box.69 * @top: Top side of bounding box.70 * @bottom: Bottom side of bounding box.71 * @fb_left: Left side of the framebuffer/content bounding box72 * @fb_top: Top of the framebuffer/content bounding box73 * @pitch: framebuffer pitch (stride)74 * @buf: buffer object when DMA-ing between buffer and screen targets.75 * @sid: Surface ID when copying between surface and screen targets.76 */77struct vmw_stdu_dirty {78 struct vmw_kms_dirty base;79 s32 left, right, top, bottom;80 s32 fb_left, fb_top;81 u32 pitch;82 union {83 struct vmw_bo *buf;84 u32 sid;85 };86};87 88/*89 * SVGA commands that are used by this code. Please see the device headers90 * for explanation.91 */92struct vmw_stdu_update {93 SVGA3dCmdHeader header;94 SVGA3dCmdUpdateGBScreenTarget body;95};96 97struct vmw_stdu_surface_copy {98 SVGA3dCmdHeader header;99 SVGA3dCmdSurfaceCopy body;100};101 102struct vmw_stdu_update_gb_image {103 SVGA3dCmdHeader header;104 SVGA3dCmdUpdateGBImage body;105};106 107/**108 * struct vmw_screen_target_display_unit - conglomerated STDU structure109 *110 * @base: VMW specific DU structure111 * @display_srf: surface to be displayed. The dimension of this will always112 * match the display mode. If the display mode matches113 * content_vfbs dimensions, then this is a pointer into the114 * corresponding field in content_vfbs. If not, then this115 * is a separate buffer to which content_vfbs will blit to.116 * @content_fb_type: content_fb type117 * @display_width: display width118 * @display_height: display height119 * @defined: true if the current display unit has been initialized120 * @cpp: Bytes per pixel121 */122struct vmw_screen_target_display_unit {123 struct vmw_display_unit base;124 struct vmw_surface *display_srf;125 enum stdu_content_type content_fb_type;126 s32 display_width, display_height;127 128 bool defined;129 130 /* For CPU Blit */131 unsigned int cpp;132};133 134 135 136static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu);137 138 139 140/******************************************************************************141 * Screen Target Display Unit CRTC Functions142 *****************************************************************************/143 144/**145 * vmw_stdu_crtc_destroy - cleans up the STDU146 *147 * @crtc: used to get a reference to the containing STDU148 */149static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc)150{151 vmw_stdu_destroy(vmw_crtc_to_stdu(crtc));152}153 154/**155 * vmw_stdu_define_st - Defines a Screen Target156 *157 * @dev_priv: VMW DRM device158 * @stdu: display unit to create a Screen Target for159 * @mode: The mode to set.160 * @crtc_x: X coordinate of screen target relative to framebuffer origin.161 * @crtc_y: Y coordinate of screen target relative to framebuffer origin.162 *163 * Creates a STDU that we can used later. This function is called whenever the164 * framebuffer size changes.165 *166 * RETURNs:167 * 0 on success, error code on failure168 */169static int vmw_stdu_define_st(struct vmw_private *dev_priv,170 struct vmw_screen_target_display_unit *stdu,171 struct drm_display_mode *mode,172 int crtc_x, int crtc_y)173{174 struct {175 SVGA3dCmdHeader header;176 SVGA3dCmdDefineGBScreenTarget body;177 } *cmd;178 179 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));180 if (unlikely(cmd == NULL))181 return -ENOMEM;182 183 cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SCREENTARGET;184 cmd->header.size = sizeof(cmd->body);185 186 cmd->body.stid = stdu->base.unit;187 cmd->body.width = mode->hdisplay;188 cmd->body.height = mode->vdisplay;189 cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0;190 cmd->body.dpi = 0;191 cmd->body.xRoot = crtc_x;192 cmd->body.yRoot = crtc_y;193 194 stdu->base.set_gui_x = cmd->body.xRoot;195 stdu->base.set_gui_y = cmd->body.yRoot;196 197 vmw_cmd_commit(dev_priv, sizeof(*cmd));198 199 stdu->defined = true;200 stdu->display_width = mode->hdisplay;201 stdu->display_height = mode->vdisplay;202 203 return 0;204}205 206 207 208/**209 * vmw_stdu_bind_st - Binds a surface to a Screen Target210 *211 * @dev_priv: VMW DRM device212 * @stdu: display unit affected213 * @res: Buffer to bind to the screen target. Set to NULL to blank screen.214 *215 * Binding a surface to a Screen Target the same as flipping216 *217 * Returns: %0 on success or -errno code on failure218 */219static int vmw_stdu_bind_st(struct vmw_private *dev_priv,220 struct vmw_screen_target_display_unit *stdu,221 const struct vmw_resource *res)222{223 SVGA3dSurfaceImageId image;224 225 struct {226 SVGA3dCmdHeader header;227 SVGA3dCmdBindGBScreenTarget body;228 } *cmd;229 230 231 if (!stdu->defined) {232 DRM_ERROR("No screen target defined\n");233 return -EINVAL;234 }235 236 /* Set up image using information in vfb */237 memset(&image, 0, sizeof(image));238 image.sid = res ? res->id : SVGA3D_INVALID_ID;239 240 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));241 if (unlikely(cmd == NULL))242 return -ENOMEM;243 244 cmd->header.id = SVGA_3D_CMD_BIND_GB_SCREENTARGET;245 cmd->header.size = sizeof(cmd->body);246 247 cmd->body.stid = stdu->base.unit;248 cmd->body.image = image;249 250 vmw_cmd_commit(dev_priv, sizeof(*cmd));251 252 return 0;253}254 255/**256 * vmw_stdu_populate_update - populate an UPDATE_GB_SCREENTARGET command with a257 * bounding box.258 *259 * @cmd: Pointer to command stream.260 * @unit: Screen target unit.261 * @left: Left side of bounding box.262 * @right: Right side of bounding box.263 * @top: Top side of bounding box.264 * @bottom: Bottom side of bounding box.265 */266static void vmw_stdu_populate_update(void *cmd, int unit,267 s32 left, s32 right, s32 top, s32 bottom)268{269 struct vmw_stdu_update *update = cmd;270 271 update->header.id = SVGA_3D_CMD_UPDATE_GB_SCREENTARGET;272 update->header.size = sizeof(update->body);273 274 update->body.stid = unit;275 update->body.rect.x = left;276 update->body.rect.y = top;277 update->body.rect.w = right - left;278 update->body.rect.h = bottom - top;279}280 281/**282 * vmw_stdu_update_st - Full update of a Screen Target283 *284 * @dev_priv: VMW DRM device285 * @stdu: display unit affected286 *287 * This function needs to be called whenever the content of a screen288 * target has changed completely. Typically as a result of a backing289 * surface change.290 *291 * RETURNS:292 * 0 on success, error code on failure293 */294static int vmw_stdu_update_st(struct vmw_private *dev_priv,295 struct vmw_screen_target_display_unit *stdu)296{297 struct vmw_stdu_update *cmd;298 299 if (!stdu->defined) {300 DRM_ERROR("No screen target defined");301 return -EINVAL;302 }303 304 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));305 if (unlikely(cmd == NULL))306 return -ENOMEM;307 308 vmw_stdu_populate_update(cmd, stdu->base.unit,309 0, stdu->display_width,310 0, stdu->display_height);311 312 vmw_cmd_commit(dev_priv, sizeof(*cmd));313 314 return 0;315}316 317 318 319/**320 * vmw_stdu_destroy_st - Destroy a Screen Target321 *322 * @dev_priv: VMW DRM device323 * @stdu: display unit to destroy324 *325 * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if326 * interrupted.327 */328static int vmw_stdu_destroy_st(struct vmw_private *dev_priv,329 struct vmw_screen_target_display_unit *stdu)330{331 int ret;332 333 struct {334 SVGA3dCmdHeader header;335 SVGA3dCmdDestroyGBScreenTarget body;336 } *cmd;337 338 339 /* Nothing to do if not successfully defined */340 if (unlikely(!stdu->defined))341 return 0;342 343 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));344 if (unlikely(cmd == NULL))345 return -ENOMEM;346 347 cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SCREENTARGET;348 cmd->header.size = sizeof(cmd->body);349 350 cmd->body.stid = stdu->base.unit;351 352 vmw_cmd_commit(dev_priv, sizeof(*cmd));353 354 /* Force sync */355 ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ);356 if (unlikely(ret != 0))357 DRM_ERROR("Failed to sync with HW");358 359 stdu->defined = false;360 stdu->display_width = 0;361 stdu->display_height = 0;362 363 return ret;364}365 366 367/**368 * vmw_stdu_crtc_mode_set_nofb - Updates screen target size369 *370 * @crtc: CRTC associated with the screen target371 *372 * This function defines/destroys a screen target373 *374 */375static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)376{377 struct vmw_private *dev_priv;378 struct vmw_screen_target_display_unit *stdu;379 struct drm_connector_state *conn_state;380 struct vmw_connector_state *vmw_conn_state;381 int x, y, ret;382 383 stdu = vmw_crtc_to_stdu(crtc);384 dev_priv = vmw_priv(crtc->dev);385 conn_state = stdu->base.connector.state;386 vmw_conn_state = vmw_connector_state_to_vcs(conn_state);387 388 if (stdu->defined) {389 ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);390 if (ret)391 DRM_ERROR("Failed to blank CRTC\n");392 393 (void) vmw_stdu_update_st(dev_priv, stdu);394 395 ret = vmw_stdu_destroy_st(dev_priv, stdu);396 if (ret)397 DRM_ERROR("Failed to destroy Screen Target\n");398 399 stdu->content_fb_type = SAME_AS_DISPLAY;400 }401 402 if (!crtc->state->enable)403 return;404 405 x = vmw_conn_state->gui_x;406 y = vmw_conn_state->gui_y;407 408 vmw_svga_enable(dev_priv);409 ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, x, y);410 411 if (ret)412 DRM_ERROR("Failed to define Screen Target of size %dx%d\n",413 crtc->x, crtc->y);414}415 416static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,417 struct drm_atomic_state *state)418{419 struct vmw_private *dev_priv;420 struct vmw_screen_target_display_unit *stdu;421 struct drm_crtc_state *new_crtc_state;422 int ret;423 424 if (!crtc) {425 DRM_ERROR("CRTC is NULL\n");426 return;427 }428 429 stdu = vmw_crtc_to_stdu(crtc);430 dev_priv = vmw_priv(crtc->dev);431 new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);432 433 if (dev_priv->vkms_enabled)434 drm_crtc_vblank_off(crtc);435 436 if (stdu->defined) {437 ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);438 if (ret)439 DRM_ERROR("Failed to blank CRTC\n");440 441 (void) vmw_stdu_update_st(dev_priv, stdu);442 443 /* Don't destroy the Screen Target if we are only setting the444 * display as inactive445 */446 if (new_crtc_state->enable &&447 !new_crtc_state->active &&448 !new_crtc_state->mode_changed)449 return;450 451 ret = vmw_stdu_destroy_st(dev_priv, stdu);452 if (ret)453 DRM_ERROR("Failed to destroy Screen Target\n");454 455 stdu->content_fb_type = SAME_AS_DISPLAY;456 }457}458 459/**460 * vmw_stdu_bo_cpu_clip - Callback to encode a CPU blit461 *462 * @dirty: The closure structure.463 *464 * This function calculates the bounding box for all the incoming clips.465 */466static void vmw_stdu_bo_cpu_clip(struct vmw_kms_dirty *dirty)467{468 struct vmw_stdu_dirty *ddirty =469 container_of(dirty, struct vmw_stdu_dirty, base);470 471 dirty->num_hits = 1;472 473 /* Calculate destination bounding box */474 ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1);475 ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1);476 ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2);477 ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2);478 479 /*480 * Calculate content bounding box. We only need the top-left481 * coordinate because width and height will be the same as the482 * destination bounding box above483 */484 ddirty->fb_left = min_t(s32, ddirty->fb_left, dirty->fb_x);485 ddirty->fb_top = min_t(s32, ddirty->fb_top, dirty->fb_y);486}487 488 489/**490 * vmw_stdu_bo_cpu_commit - Callback to do a CPU blit from buffer object491 *492 * @dirty: The closure structure.493 *494 * For the special case when we cannot create a proxy surface in a495 * 2D VM, we have to do a CPU blit ourselves.496 */497static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty)498{499 struct vmw_stdu_dirty *ddirty =500 container_of(dirty, struct vmw_stdu_dirty, base);501 struct vmw_screen_target_display_unit *stdu =502 container_of(dirty->unit, typeof(*stdu), base);503 s32 width, height;504 s32 src_pitch, dst_pitch;505 struct vmw_bo *src_bo, *dst_bo;506 u32 src_offset, dst_offset;507 struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(stdu->cpp);508 509 if (!dirty->num_hits)510 return;511 512 width = ddirty->right - ddirty->left;513 height = ddirty->bottom - ddirty->top;514 515 if (width == 0 || height == 0)516 return;517 518 /* Assume we are blitting from Guest (bo) to Host (display_srf) */519 src_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp;520 src_bo = stdu->display_srf->res.guest_memory_bo;521 src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp;522 523 dst_pitch = ddirty->pitch;524 dst_bo = ddirty->buf;525 dst_offset = ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp;526 527 (void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch,528 src_bo, src_offset, src_pitch,529 width * stdu->cpp, height, &diff);530}531 532/**533 * vmw_kms_stdu_readback - Perform a readback from a buffer-object backed534 * framebuffer and the screen target system.535 *536 * @dev_priv: Pointer to the device private structure.537 * @file_priv: Pointer to a struct drm-file identifying the caller. May be538 * set to NULL, but then @user_fence_rep must also be set to NULL.539 * @vfb: Pointer to the buffer-object backed framebuffer.540 * @user_fence_rep: User-space provided structure for fence information.541 * @clips: Array of clip rects. Either @clips or @vclips must be NULL.542 * @vclips: Alternate array of clip rects. Either @clips or @vclips must543 * be NULL.544 * @num_clips: Number of clip rects in @clips or @vclips.545 * @increment: Increment to use when looping over @clips or @vclips.546 * @crtc: If crtc is passed, perform stdu dma on that crtc only.547 *548 * If DMA-ing till the screen target system, the function will also notify549 * the screen target system that a bounding box of the cliprects has been550 * updated.551 *552 * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if553 * interrupted.554 */555int vmw_kms_stdu_readback(struct vmw_private *dev_priv,556 struct drm_file *file_priv,557 struct vmw_framebuffer *vfb,558 struct drm_vmw_fence_rep __user *user_fence_rep,559 struct drm_clip_rect *clips,560 struct drm_vmw_rect *vclips,561 uint32_t num_clips,562 int increment,563 struct drm_crtc *crtc)564{565 struct vmw_bo *buf =566 container_of(vfb, struct vmw_framebuffer_bo, base)->buffer;567 struct vmw_stdu_dirty ddirty;568 int ret;569 DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);570 571 /*572 * The GMR domain might seem confusing because it might seem like it should573 * never happen with screen targets but e.g. the xorg vmware driver issues574 * CMD_SURFACE_DMA for various pixmap updates which might transition our bo to575 * a GMR. Instead of forcing another transition we can optimize the readback576 * by reading directly from the GMR.577 */578 vmw_bo_placement_set(buf,579 VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR,580 VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR);581 ret = vmw_validation_add_bo(&val_ctx, buf);582 if (ret)583 return ret;584 585 ret = vmw_validation_prepare(&val_ctx, NULL, true);586 if (ret)587 goto out_unref;588 589 ddirty.left = ddirty.top = S32_MAX;590 ddirty.right = ddirty.bottom = S32_MIN;591 ddirty.fb_left = ddirty.fb_top = S32_MAX;592 ddirty.pitch = vfb->base.pitches[0];593 ddirty.buf = buf;594 595 ddirty.base.fifo_commit = vmw_stdu_bo_cpu_commit;596 ddirty.base.clip = vmw_stdu_bo_cpu_clip;597 ddirty.base.fifo_reserve_size = 0;598 599 ddirty.base.crtc = crtc;600 601 ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips,602 0, 0, num_clips, increment, &ddirty.base);603 604 vmw_kms_helper_validation_finish(dev_priv, file_priv, &val_ctx, NULL,605 user_fence_rep);606 return ret;607 608out_unref:609 vmw_validation_unref_lists(&val_ctx);610 return ret;611}612 613/**614 * vmw_kms_stdu_surface_clip - Callback to encode a surface copy command cliprect615 *616 * @dirty: The closure structure.617 *618 * Encodes a surface copy command cliprect and updates the bounding box619 * for the copy.620 */621static void vmw_kms_stdu_surface_clip(struct vmw_kms_dirty *dirty)622{623 struct vmw_stdu_dirty *sdirty =624 container_of(dirty, struct vmw_stdu_dirty, base);625 struct vmw_stdu_surface_copy *cmd = dirty->cmd;626 struct vmw_screen_target_display_unit *stdu =627 container_of(dirty->unit, typeof(*stdu), base);628 629 if (sdirty->sid != stdu->display_srf->res.id) {630 struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];631 632 blit += dirty->num_hits;633 blit->srcx = dirty->fb_x;634 blit->srcy = dirty->fb_y;635 blit->x = dirty->unit_x1;636 blit->y = dirty->unit_y1;637 blit->d = 1;638 blit->w = dirty->unit_x2 - dirty->unit_x1;639 blit->h = dirty->unit_y2 - dirty->unit_y1;640 }641 642 dirty->num_hits++;643 644 /* Destination bounding box */645 sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1);646 sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1);647 sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2);648 sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2);649}650 651/**652 * vmw_kms_stdu_surface_fifo_commit - Callback to fill in and submit a surface653 * copy command.654 *655 * @dirty: The closure structure.656 *657 * Fills in the missing fields in a surface copy command, and encodes a screen658 * target update command.659 */660static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty)661{662 struct vmw_stdu_dirty *sdirty =663 container_of(dirty, struct vmw_stdu_dirty, base);664 struct vmw_screen_target_display_unit *stdu =665 container_of(dirty->unit, typeof(*stdu), base);666 struct vmw_stdu_surface_copy *cmd = dirty->cmd;667 struct vmw_stdu_update *update;668 size_t blit_size = sizeof(SVGA3dCopyBox) * dirty->num_hits;669 size_t commit_size;670 671 if (!dirty->num_hits) {672 vmw_cmd_commit(dirty->dev_priv, 0);673 return;674 }675 676 if (sdirty->sid != stdu->display_srf->res.id) {677 struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];678 679 cmd->header.id = SVGA_3D_CMD_SURFACE_COPY;680 cmd->header.size = sizeof(cmd->body) + blit_size;681 cmd->body.src.sid = sdirty->sid;682 cmd->body.dest.sid = stdu->display_srf->res.id;683 update = (struct vmw_stdu_update *) &blit[dirty->num_hits];684 commit_size = sizeof(*cmd) + blit_size + sizeof(*update);685 stdu->display_srf->res.res_dirty = true;686 } else {687 update = dirty->cmd;688 commit_size = sizeof(*update);689 }690 691 vmw_stdu_populate_update(update, stdu->base.unit, sdirty->left,692 sdirty->right, sdirty->top, sdirty->bottom);693 694 vmw_cmd_commit(dirty->dev_priv, commit_size);695 696 sdirty->left = sdirty->top = S32_MAX;697 sdirty->right = sdirty->bottom = S32_MIN;698}699 700/**701 * vmw_kms_stdu_surface_dirty - Dirty part of a surface backed framebuffer702 *703 * @dev_priv: Pointer to the device private structure.704 * @framebuffer: Pointer to the surface-buffer backed framebuffer.705 * @clips: Array of clip rects. Either @clips or @vclips must be NULL.706 * @vclips: Alternate array of clip rects. Either @clips or @vclips must707 * be NULL.708 * @srf: Pointer to surface to blit from. If NULL, the surface attached709 * to @framebuffer will be used.710 * @dest_x: X coordinate offset to align @srf with framebuffer coordinates.711 * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates.712 * @num_clips: Number of clip rects in @clips.713 * @inc: Increment to use when looping over @clips.714 * @out_fence: If non-NULL, will return a ref-counted pointer to a715 * struct vmw_fence_obj. The returned fence pointer may be NULL in which716 * case the device has already synchronized.717 * @crtc: If crtc is passed, perform surface dirty on that crtc only.718 *719 * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if720 * interrupted.721 */722int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,723 struct vmw_framebuffer *framebuffer,724 struct drm_clip_rect *clips,725 struct drm_vmw_rect *vclips,726 struct vmw_resource *srf,727 s32 dest_x,728 s32 dest_y,729 unsigned num_clips, int inc,730 struct vmw_fence_obj **out_fence,731 struct drm_crtc *crtc)732{733 struct vmw_framebuffer_surface *vfbs =734 container_of(framebuffer, typeof(*vfbs), base);735 struct vmw_stdu_dirty sdirty;736 DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);737 int ret;738 739 if (!srf)740 srf = &vmw_user_object_surface(&vfbs->uo)->res;741 742 ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE,743 NULL, NULL);744 if (ret)745 return ret;746 747 ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true);748 if (ret)749 goto out_unref;750 751 sdirty.base.fifo_commit = vmw_kms_stdu_surface_fifo_commit;752 sdirty.base.clip = vmw_kms_stdu_surface_clip;753 sdirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_surface_copy) +754 sizeof(SVGA3dCopyBox) * num_clips +755 sizeof(struct vmw_stdu_update);756 sdirty.base.crtc = crtc;757 sdirty.sid = srf->id;758 sdirty.left = sdirty.top = S32_MAX;759 sdirty.right = sdirty.bottom = S32_MIN;760 761 ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,762 dest_x, dest_y, num_clips, inc,763 &sdirty.base);764 765 vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence,766 NULL);767 768 return ret;769 770out_unref:771 vmw_validation_unref_lists(&val_ctx);772 return ret;773}774 775/*776 * Screen Target CRTC dispatch table777 */778static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {779 .gamma_set = vmw_du_crtc_gamma_set,780 .destroy = vmw_stdu_crtc_destroy,781 .reset = vmw_du_crtc_reset,782 .atomic_duplicate_state = vmw_du_crtc_duplicate_state,783 .atomic_destroy_state = vmw_du_crtc_destroy_state,784 .set_config = drm_atomic_helper_set_config,785 .page_flip = drm_atomic_helper_page_flip,786 .enable_vblank = vmw_vkms_enable_vblank,787 .disable_vblank = vmw_vkms_disable_vblank,788 .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,789 .get_crc_sources = vmw_vkms_get_crc_sources,790 .set_crc_source = vmw_vkms_set_crc_source,791 .verify_crc_source = vmw_vkms_verify_crc_source,792};793 794 795 796/******************************************************************************797 * Screen Target Display Unit Encoder Functions798 *****************************************************************************/799 800/**801 * vmw_stdu_encoder_destroy - cleans up the STDU802 *803 * @encoder: used the get the containing STDU804 *805 * vmwgfx cleans up crtc/encoder/connector all at the same time so technically806 * this can be a no-op. Nevertheless, it doesn't hurt of have this in case807 * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't808 * get called.809 */810static void vmw_stdu_encoder_destroy(struct drm_encoder *encoder)811{812 vmw_stdu_destroy(vmw_encoder_to_stdu(encoder));813}814 815static const struct drm_encoder_funcs vmw_stdu_encoder_funcs = {816 .destroy = vmw_stdu_encoder_destroy,817};818 819 820 821/******************************************************************************822 * Screen Target Display Unit Connector Functions823 *****************************************************************************/824 825/**826 * vmw_stdu_connector_destroy - cleans up the STDU827 *828 * @connector: used to get the containing STDU829 *830 * vmwgfx cleans up crtc/encoder/connector all at the same time so technically831 * this can be a no-op. Nevertheless, it doesn't hurt of have this in case832 * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't833 * get called.834 */835static void vmw_stdu_connector_destroy(struct drm_connector *connector)836{837 vmw_stdu_destroy(vmw_connector_to_stdu(connector));838}839 840static enum drm_mode_status841vmw_stdu_connector_mode_valid(struct drm_connector *connector,842 struct drm_display_mode *mode)843{844 enum drm_mode_status ret;845 struct drm_device *dev = connector->dev;846 struct vmw_private *dev_priv = vmw_priv(dev);847 u64 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4;848 /* Align width and height to account for GPU tile over-alignment */849 u64 required_mem = ALIGN(mode->hdisplay, GPU_TILE_SIZE) *850 ALIGN(mode->vdisplay, GPU_TILE_SIZE) *851 assumed_cpp;852 required_mem = ALIGN(required_mem, PAGE_SIZE);853 854 ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width,855 dev_priv->stdu_max_height);856 if (ret != MODE_OK)857 return ret;858 859 ret = drm_mode_validate_size(mode, dev_priv->texture_max_width,860 dev_priv->texture_max_height);861 if (ret != MODE_OK)862 return ret;863 864 if (required_mem > dev_priv->max_primary_mem)865 return MODE_MEM;866 867 if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE)868 return MODE_MEM;869 870 if (required_mem > dev_priv->max_mob_size)871 return MODE_MEM;872 873 return MODE_OK;874}875 876/*877 * Trigger a modeset if the X,Y position of the Screen Target changes.878 * This is needed when multi-mon is cycled. The original Screen Target will have879 * the same mode but its relative X,Y position in the topology will change.880 */881static int vmw_stdu_connector_atomic_check(struct drm_connector *conn,882 struct drm_atomic_state *state)883{884 struct drm_connector_state *conn_state;885 struct vmw_screen_target_display_unit *du;886 struct drm_crtc_state *new_crtc_state;887 888 conn_state = drm_atomic_get_connector_state(state, conn);889 890 if (IS_ERR(conn_state))891 return PTR_ERR(conn_state);892 893 du = vmw_connector_to_stdu(conn);894 895 if (!conn_state->crtc)896 return 0;897 898 new_crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);899 if (du->base.gui_x != du->base.set_gui_x ||900 du->base.gui_y != du->base.set_gui_y)901 new_crtc_state->mode_changed = true;902 903 return 0;904}905 906static const struct drm_connector_funcs vmw_stdu_connector_funcs = {907 .dpms = vmw_du_connector_dpms,908 .detect = vmw_du_connector_detect,909 .fill_modes = drm_helper_probe_single_connector_modes,910 .destroy = vmw_stdu_connector_destroy,911 .reset = vmw_du_connector_reset,912 .atomic_duplicate_state = vmw_du_connector_duplicate_state,913 .atomic_destroy_state = vmw_du_connector_destroy_state,914};915 916 917static const struct918drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = {919 .get_modes = vmw_connector_get_modes,920 .mode_valid = vmw_stdu_connector_mode_valid,921 .atomic_check = vmw_stdu_connector_atomic_check,922};923 924 925 926/******************************************************************************927 * Screen Target Display Plane Functions928 *****************************************************************************/929 930 931 932/**933 * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface934 *935 * @plane: display plane936 * @old_state: Contains the FB to clean up937 *938 * Unpins the display surface939 *940 * Returns 0 on success941 */942static void943vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,944 struct drm_plane_state *old_state)945{946 struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);947 948 if (vmw_user_object_surface(&vps->uo))949 WARN_ON(!vps->pinned);950 vmw_du_plane_cleanup_fb(plane, old_state);951 952 vps->content_fb_type = SAME_AS_DISPLAY;953 vps->cpp = 0;954}955 956 957/**958 * vmw_stdu_primary_plane_prepare_fb - Readies the display surface959 *960 * @plane: display plane961 * @new_state: info on the new plane state, including the FB962 *963 * This function allocates a new display surface if the content is964 * backed by a buffer object. The display surface is pinned here, and it'll965 * be unpinned in .cleanup_fb()966 *967 * Returns: %0 on success968 */969static int970vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,971 struct drm_plane_state *new_state)972{973 struct vmw_private *dev_priv = vmw_priv(plane->dev);974 struct drm_framebuffer *new_fb = new_state->fb;975 struct vmw_framebuffer *vfb;976 struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);977 enum stdu_content_type new_content_type;978 struct vmw_framebuffer_surface *new_vfbs;979 uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;980 struct drm_plane_state *old_state = plane->state;981 struct drm_rect rect;982 int ret;983 984 /* No FB to prepare */985 if (!new_fb) {986 if (vmw_user_object_surface(&vps->uo)) {987 WARN_ON(vps->pinned != 0);988 vmw_user_object_unref(&vps->uo);989 }990 991 return 0;992 }993 994 vfb = vmw_framebuffer_to_vfb(new_fb);995 new_vfbs = (vfb->bo) ? NULL : vmw_framebuffer_to_vfbs(new_fb);996 997 if (new_vfbs &&998 vmw_user_object_surface(&new_vfbs->uo)->metadata.base_size.width == hdisplay &&999 vmw_user_object_surface(&new_vfbs->uo)->metadata.base_size.height == vdisplay)1000 new_content_type = SAME_AS_DISPLAY;1001 else if (vfb->bo)1002 new_content_type = SEPARATE_BO;1003 else1004 new_content_type = SEPARATE_SURFACE;1005 1006 if (new_content_type != SAME_AS_DISPLAY) {1007 struct vmw_surface_metadata metadata = {0};1008 1009 /*1010 * If content buffer is a buffer object, then we have to1011 * construct surface info1012 */1013 if (new_content_type == SEPARATE_BO) {1014 1015 switch (new_fb->format->cpp[0]*8) {1016 case 32:1017 metadata.format = SVGA3D_X8R8G8B8;1018 break;1019 1020 case 16:1021 metadata.format = SVGA3D_R5G6B5;1022 break;1023 1024 case 8:1025 metadata.format = SVGA3D_P8;1026 break;1027 1028 default:1029 DRM_ERROR("Invalid format\n");1030 return -EINVAL;1031 }1032 1033 metadata.mip_levels[0] = 1;1034 metadata.num_sizes = 1;1035 metadata.scanout = true;1036 } else {1037 metadata = vmw_user_object_surface(&new_vfbs->uo)->metadata;1038 }1039 1040 metadata.base_size.width = hdisplay;1041 metadata.base_size.height = vdisplay;1042 metadata.base_size.depth = 1;1043 1044 if (vmw_user_object_surface(&vps->uo)) {1045 struct drm_vmw_size cur_base_size =1046 vmw_user_object_surface(&vps->uo)->metadata.base_size;1047 1048 if (cur_base_size.width != metadata.base_size.width ||1049 cur_base_size.height != metadata.base_size.height ||1050 vmw_user_object_surface(&vps->uo)->metadata.format != metadata.format) {1051 WARN_ON(vps->pinned != 0);1052 vmw_user_object_unref(&vps->uo);1053 }1054 1055 }1056 1057 if (!vmw_user_object_surface(&vps->uo)) {1058 ret = vmw_gb_surface_define(dev_priv, &metadata,1059 &vps->uo.surface);1060 if (ret != 0) {1061 DRM_ERROR("Couldn't allocate STDU surface.\n");1062 return ret;1063 }1064 }1065 } else {1066 /*1067 * prepare_fb and clean_fb should only take care of pinning1068 * and unpinning. References are tracked by state objects.1069 * The only time we add a reference in prepare_fb is if the1070 * state object doesn't have a reference to begin with1071 */1072 if (vmw_user_object_surface(&vps->uo)) {1073 WARN_ON(vps->pinned != 0);1074 vmw_user_object_unref(&vps->uo);1075 }1076 1077 memcpy(&vps->uo, &new_vfbs->uo, sizeof(vps->uo));1078 vmw_user_object_ref(&vps->uo);1079 }1080 1081 if (vmw_user_object_surface(&vps->uo)) {1082 1083 /* Pin new surface before flipping */1084 ret = vmw_resource_pin(&vmw_user_object_surface(&vps->uo)->res, false);1085 if (ret)1086 goto out_srf_unref;1087 1088 vps->pinned++;1089 }1090 1091 vps->content_fb_type = new_content_type;1092 1093 /*1094 * The drm fb code will do blit's via the vmap interface, which doesn't1095 * trigger vmw_bo page dirty tracking due to being kernel side (and thus1096 * doesn't require mmap'ing) so we have to update the surface's dirty1097 * regions by hand but we want to be careful to not overwrite the1098 * resource if it has been written to by the gpu (res_dirty).1099 */1100 if (vps->uo.buffer && vps->uo.buffer->is_dumb) {1101 struct vmw_surface *surf = vmw_user_object_surface(&vps->uo);1102 struct vmw_resource *res = &surf->res;1103 1104 if (!res->res_dirty && drm_atomic_helper_damage_merged(old_state,1105 new_state,1106 &rect)) {1107 /*1108 * At some point it might be useful to actually translate1109 * (rect.x1, rect.y1) => start, and (rect.x2, rect.y2) => end,1110 * but currently the fb code will just report the entire fb1111 * dirty so in practice it doesn't matter.1112 */1113 pgoff_t start = res->guest_memory_offset >> PAGE_SHIFT;1114 pgoff_t end = __KERNEL_DIV_ROUND_UP(res->guest_memory_offset +1115 res->guest_memory_size,1116 PAGE_SIZE);1117 vmw_resource_dirty_update(res, start, end);1118 }1119 }1120 1121 /*1122 * This should only happen if the buffer object is too large to create a1123 * proxy surface for.1124 */1125 if (vps->content_fb_type == SEPARATE_BO)1126 vps->cpp = new_fb->pitches[0] / new_fb->width;1127 1128 return 0;1129 1130out_srf_unref:1131 vmw_user_object_unref(&vps->uo);1132 return ret;1133}1134 1135static uint32_t vmw_stdu_bo_fifo_size_cpu(struct vmw_du_update_plane *update,1136 uint32_t num_hits)1137{1138 return sizeof(struct vmw_stdu_update_gb_image) +1139 sizeof(struct vmw_stdu_update);1140}1141 1142static uint32_t vmw_stdu_bo_pre_clip_cpu(struct vmw_du_update_plane *update,1143 void *cmd, uint32_t num_hits)1144{1145 struct vmw_du_update_plane_buffer *bo_update =1146 container_of(update, typeof(*bo_update), base);1147 1148 bo_update->fb_left = INT_MAX;1149 bo_update->fb_top = INT_MAX;1150 1151 return 0;1152}1153 1154static uint32_t vmw_stdu_bo_clip_cpu(struct vmw_du_update_plane *update,1155 void *cmd, struct drm_rect *clip,1156 uint32_t fb_x, uint32_t fb_y)1157{1158 struct vmw_du_update_plane_buffer *bo_update =1159 container_of(update, typeof(*bo_update), base);1160 1161 bo_update->fb_left = min_t(int, bo_update->fb_left, fb_x);1162 bo_update->fb_top = min_t(int, bo_update->fb_top, fb_y);1163 1164 return 0;1165}1166 1167static uint32_t1168vmw_stdu_bo_populate_update_cpu(struct vmw_du_update_plane *update, void *cmd,1169 struct drm_rect *bb)1170{1171 struct vmw_du_update_plane_buffer *bo_update;1172 struct vmw_screen_target_display_unit *stdu;1173 struct vmw_framebuffer_bo *vfbbo;1174 struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(0);1175 struct vmw_stdu_update_gb_image *cmd_img = cmd;1176 struct vmw_stdu_update *cmd_update;1177 struct vmw_bo *src_bo, *dst_bo;1178 u32 src_offset, dst_offset;1179 s32 src_pitch, dst_pitch;1180 s32 width, height;1181 1182 bo_update = container_of(update, typeof(*bo_update), base);1183 stdu = container_of(update->du, typeof(*stdu), base);1184 vfbbo = container_of(update->vfb, typeof(*vfbbo), base);1185 1186 width = bb->x2 - bb->x1;1187 height = bb->y2 - bb->y1;1188 1189 diff.cpp = stdu->cpp;1190 1191 dst_bo = stdu->display_srf->res.guest_memory_bo;1192 dst_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp;1193 dst_offset = bb->y1 * dst_pitch + bb->x1 * stdu->cpp;1194 1195 src_bo = vfbbo->buffer;1196 src_pitch = update->vfb->base.pitches[0];1197 src_offset = bo_update->fb_top * src_pitch + bo_update->fb_left *1198 stdu->cpp;1199 1200 (void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch, src_bo,1201 src_offset, src_pitch, width * stdu->cpp, height,1202 &diff);1203 1204 if (drm_rect_visible(&diff.rect)) {1205 SVGA3dBox *box = &cmd_img->body.box;1206 1207 cmd_img->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE;1208 cmd_img->header.size = sizeof(cmd_img->body);1209 cmd_img->body.image.sid = stdu->display_srf->res.id;1210 cmd_img->body.image.face = 0;1211 cmd_img->body.image.mipmap = 0;1212 1213 box->x = diff.rect.x1;1214 box->y = diff.rect.y1;1215 box->z = 0;1216 box->w = drm_rect_width(&diff.rect);1217 box->h = drm_rect_height(&diff.rect);1218 box->d = 1;1219 1220 cmd_update = (struct vmw_stdu_update *)&cmd_img[1];1221 vmw_stdu_populate_update(cmd_update, stdu->base.unit,1222 diff.rect.x1, diff.rect.x2,1223 diff.rect.y1, diff.rect.y2);1224 1225 return sizeof(*cmd_img) + sizeof(*cmd_update);1226 }1227 1228 return 0;1229}1230 1231/**1232 * vmw_stdu_plane_update_bo - Update display unit for bo backed fb.1233 * @dev_priv: device private.1234 * @plane: plane state.1235 * @old_state: old plane state.1236 * @vfb: framebuffer which is blitted to display unit.1237 * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj.1238 * The returned fence pointer may be NULL in which case the device1239 * has already synchronized.1240 *1241 * Return: 0 on success or a negative error code on failure.1242 */1243static int vmw_stdu_plane_update_bo(struct vmw_private *dev_priv,1244 struct drm_plane *plane,1245 struct drm_plane_state *old_state,1246 struct vmw_framebuffer *vfb,1247 struct vmw_fence_obj **out_fence)1248{1249 struct vmw_du_update_plane_buffer bo_update;1250 1251 memset(&bo_update, 0, sizeof(struct vmw_du_update_plane_buffer));1252 bo_update.base.plane = plane;1253 bo_update.base.old_state = old_state;1254 bo_update.base.dev_priv = dev_priv;1255 bo_update.base.du = vmw_crtc_to_du(plane->state->crtc);1256 bo_update.base.vfb = vfb;1257 bo_update.base.out_fence = out_fence;1258 bo_update.base.mutex = NULL;1259 bo_update.base.intr = false;1260 1261 bo_update.base.calc_fifo_size = vmw_stdu_bo_fifo_size_cpu;1262 bo_update.base.pre_clip = vmw_stdu_bo_pre_clip_cpu;1263 bo_update.base.clip = vmw_stdu_bo_clip_cpu;1264 bo_update.base.post_clip = vmw_stdu_bo_populate_update_cpu;1265 1266 return vmw_du_helper_plane_update(&bo_update.base);1267}1268 1269static uint32_t1270vmw_stdu_surface_fifo_size_same_display(struct vmw_du_update_plane *update,1271 uint32_t num_hits)1272{1273 uint32_t size = 0;1274 1275 size += sizeof(struct vmw_stdu_update);1276 1277 return size;1278}1279 1280static uint32_t vmw_stdu_surface_fifo_size(struct vmw_du_update_plane *update,1281 uint32_t num_hits)1282{1283 uint32_t size = 0;1284 1285 size += sizeof(struct vmw_stdu_surface_copy) + sizeof(SVGA3dCopyBox) *1286 num_hits + sizeof(struct vmw_stdu_update);1287 1288 return size;1289}1290 1291static uint32_t1292vmw_stdu_surface_populate_copy(struct vmw_du_update_plane *update, void *cmd,1293 uint32_t num_hits)1294{1295 struct vmw_screen_target_display_unit *stdu;1296 struct vmw_framebuffer_surface *vfbs;1297 struct vmw_stdu_surface_copy *cmd_copy = cmd;1298 1299 stdu = container_of(update->du, typeof(*stdu), base);1300 vfbs = container_of(update->vfb, typeof(*vfbs), base);1301 1302 cmd_copy->header.id = SVGA_3D_CMD_SURFACE_COPY;1303 cmd_copy->header.size = sizeof(cmd_copy->body) + sizeof(SVGA3dCopyBox) *1304 num_hits;1305 cmd_copy->body.src.sid = vmw_user_object_surface(&vfbs->uo)->res.id;1306 cmd_copy->body.dest.sid = stdu->display_srf->res.id;1307 1308 return sizeof(*cmd_copy);1309}1310 1311static uint32_t1312vmw_stdu_surface_populate_clip(struct vmw_du_update_plane *update, void *cmd,1313 struct drm_rect *clip, uint32_t fb_x,1314 uint32_t fb_y)1315{1316 struct SVGA3dCopyBox *box = cmd;1317 1318 box->srcx = fb_x;1319 box->srcy = fb_y;1320 box->srcz = 0;1321 box->x = clip->x1;1322 box->y = clip->y1;1323 box->z = 0;1324 box->w = drm_rect_width(clip);1325 box->h = drm_rect_height(clip);1326 box->d = 1;1327 1328 return sizeof(*box);1329}1330 1331static uint32_t1332vmw_stdu_surface_populate_update(struct vmw_du_update_plane *update, void *cmd,1333 struct drm_rect *bb)1334{1335 vmw_stdu_populate_update(cmd, update->du->unit, bb->x1, bb->x2, bb->y1,1336 bb->y2);1337 1338 return sizeof(struct vmw_stdu_update);1339}1340 1341/**1342 * vmw_stdu_plane_update_surface - Update display unit for surface backed fb1343 * @dev_priv: Device private1344 * @plane: Plane state1345 * @old_state: Old plane state1346 * @vfb: Framebuffer which is blitted to display unit1347 * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj.1348 * The returned fence pointer may be NULL in which case the device1349 * has already synchronized.1350 *1351 * Return: 0 on success or a negative error code on failure.1352 */1353static int vmw_stdu_plane_update_surface(struct vmw_private *dev_priv,1354 struct drm_plane *plane,1355 struct drm_plane_state *old_state,1356 struct vmw_framebuffer *vfb,1357 struct vmw_fence_obj **out_fence)1358{1359 struct vmw_du_update_plane srf_update;1360 struct vmw_screen_target_display_unit *stdu;1361 struct vmw_framebuffer_surface *vfbs;1362 1363 stdu = vmw_crtc_to_stdu(plane->state->crtc);1364 vfbs = container_of(vfb, typeof(*vfbs), base);1365 1366 memset(&srf_update, 0, sizeof(struct vmw_du_update_plane));1367 srf_update.plane = plane;1368 srf_update.old_state = old_state;1369 srf_update.dev_priv = dev_priv;1370 srf_update.du = vmw_crtc_to_du(plane->state->crtc);1371 srf_update.vfb = vfb;1372 srf_update.out_fence = out_fence;1373 srf_update.mutex = &dev_priv->cmdbuf_mutex;1374 srf_update.intr = true;1375 1376 if (vmw_user_object_surface(&vfbs->uo)->res.id != stdu->display_srf->res.id) {1377 srf_update.calc_fifo_size = vmw_stdu_surface_fifo_size;1378 srf_update.pre_clip = vmw_stdu_surface_populate_copy;1379 srf_update.clip = vmw_stdu_surface_populate_clip;1380 } else {1381 srf_update.calc_fifo_size =1382 vmw_stdu_surface_fifo_size_same_display;1383 }1384 1385 srf_update.post_clip = vmw_stdu_surface_populate_update;1386 1387 return vmw_du_helper_plane_update(&srf_update);1388}1389 1390/**1391 * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane1392 * @plane: display plane1393 * @state: Only used to get crtc info1394 *1395 * Formally update stdu->display_srf to the new plane, and bind the new1396 * plane STDU. This function is called during the commit phase when1397 * all the preparation have been done and all the configurations have1398 * been checked.1399 */1400static void1401vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,1402 struct drm_atomic_state *state)1403{1404 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);1405 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);1406 struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);1407 struct drm_crtc *crtc = new_state->crtc;1408 struct vmw_screen_target_display_unit *stdu;1409 struct vmw_fence_obj *fence = NULL;1410 struct vmw_private *dev_priv;1411 int ret;1412 1413 /* If case of device error, maintain consistent atomic state */1414 if (crtc && new_state->fb) {1415 struct vmw_framebuffer *vfb =1416 vmw_framebuffer_to_vfb(new_state->fb);1417 stdu = vmw_crtc_to_stdu(crtc);1418 dev_priv = vmw_priv(crtc->dev);1419 1420 stdu->display_srf = vmw_user_object_surface(&vps->uo);1421 stdu->content_fb_type = vps->content_fb_type;1422 stdu->cpp = vps->cpp;1423 1424 ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res);1425 if (ret)1426 DRM_ERROR("Failed to bind surface to STDU.\n");1427 1428 if (vfb->bo)1429 ret = vmw_stdu_plane_update_bo(dev_priv, plane,1430 old_state, vfb, &fence);1431 else1432 ret = vmw_stdu_plane_update_surface(dev_priv, plane,1433 old_state, vfb,1434 &fence);1435 if (ret)1436 DRM_ERROR("Failed to update STDU.\n");1437 } else {1438 crtc = old_state->crtc;1439 stdu = vmw_crtc_to_stdu(crtc);1440 dev_priv = vmw_priv(crtc->dev);1441 1442 /* Blank STDU when fb and crtc are NULL */1443 if (!stdu->defined)1444 return;1445 1446 ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);1447 if (ret)1448 DRM_ERROR("Failed to blank STDU\n");1449 1450 ret = vmw_stdu_update_st(dev_priv, stdu);1451 if (ret)1452 DRM_ERROR("Failed to update STDU.\n");1453 1454 return;1455 }1456 1457 if (fence)1458 vmw_fence_obj_unreference(&fence);1459}1460 1461static void1462vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc,1463 struct drm_atomic_state *state)1464{1465 struct vmw_private *vmw = vmw_priv(crtc->dev);1466 struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);1467 1468 if (vmw->vkms_enabled)1469 vmw_vkms_set_crc_surface(crtc, stdu->display_srf);1470 vmw_vkms_crtc_atomic_flush(crtc, state);1471}1472 1473static const struct drm_plane_funcs vmw_stdu_plane_funcs = {1474 .update_plane = drm_atomic_helper_update_plane,1475 .disable_plane = drm_atomic_helper_disable_plane,1476 .destroy = vmw_du_primary_plane_destroy,1477 .reset = vmw_du_plane_reset,1478 .atomic_duplicate_state = vmw_du_plane_duplicate_state,1479 .atomic_destroy_state = vmw_du_plane_destroy_state,1480};1481 1482static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {1483 .update_plane = drm_atomic_helper_update_plane,1484 .disable_plane = drm_atomic_helper_disable_plane,1485 .destroy = vmw_du_cursor_plane_destroy,1486 .reset = vmw_du_plane_reset,1487 .atomic_duplicate_state = vmw_du_plane_duplicate_state,1488 .atomic_destroy_state = vmw_du_plane_destroy_state,1489};1490 1491 1492/*1493 * Atomic Helpers1494 */1495static const struct1496drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = {1497 .atomic_check = vmw_du_cursor_plane_atomic_check,1498 .atomic_update = vmw_du_cursor_plane_atomic_update,1499 .prepare_fb = vmw_du_cursor_plane_prepare_fb,1500 .cleanup_fb = vmw_du_cursor_plane_cleanup_fb,1501};1502 1503static const struct1504drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {1505 .atomic_check = vmw_du_primary_plane_atomic_check,1506 .atomic_update = vmw_stdu_primary_plane_atomic_update,1507 .prepare_fb = vmw_stdu_primary_plane_prepare_fb,1508 .cleanup_fb = vmw_stdu_primary_plane_cleanup_fb,1509};1510 1511static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {1512 .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb,1513 .atomic_check = vmw_du_crtc_atomic_check,1514 .atomic_begin = vmw_du_crtc_atomic_begin,1515 .atomic_flush = vmw_stdu_crtc_atomic_flush,1516 .atomic_enable = vmw_vkms_crtc_atomic_enable,1517 .atomic_disable = vmw_stdu_crtc_atomic_disable,1518};1519 1520 1521/**1522 * vmw_stdu_init - Sets up a Screen Target Display Unit1523 *1524 * @dev_priv: VMW DRM device1525 * @unit: unit number range from 0 to VMWGFX_NUM_DISPLAY_UNITS1526 *1527 * This function is called once per CRTC, and allocates one Screen Target1528 * display unit to represent that CRTC. Since the SVGA device does not separate1529 * out encoder and connector, they are represented as part of the STDU as well.1530 *1531 * Returns: %0 on success or -errno code on failure1532 */1533static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)1534{1535 struct vmw_screen_target_display_unit *stdu;1536 struct drm_device *dev = &dev_priv->drm;1537 struct drm_connector *connector;1538 struct drm_encoder *encoder;1539 struct drm_plane *primary;1540 struct vmw_cursor_plane *cursor;1541 struct drm_crtc *crtc;1542 int ret;1543 1544 stdu = kzalloc(sizeof(*stdu), GFP_KERNEL);1545 if (!stdu)1546 return -ENOMEM;1547 1548 stdu->base.unit = unit;1549 crtc = &stdu->base.crtc;1550 encoder = &stdu->base.encoder;1551 connector = &stdu->base.connector;1552 primary = &stdu->base.primary;1553 cursor = &stdu->base.cursor;1554 1555 stdu->base.pref_active = (unit == 0);1556 stdu->base.pref_width = dev_priv->initial_width;1557 stdu->base.pref_height = dev_priv->initial_height;1558 stdu->base.is_implicit = false;1559 1560 /* Initialize primary plane */1561 ret = drm_universal_plane_init(dev, primary,1562 0, &vmw_stdu_plane_funcs,1563 vmw_primary_plane_formats,1564 ARRAY_SIZE(vmw_primary_plane_formats),1565 NULL, DRM_PLANE_TYPE_PRIMARY, NULL);1566 if (ret) {1567 DRM_ERROR("Failed to initialize primary plane");1568 goto err_free;1569 }1570 1571 drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs);1572 drm_plane_enable_fb_damage_clips(primary);1573 1574 /* Initialize cursor plane */1575 ret = drm_universal_plane_init(dev, &cursor->base,1576 0, &vmw_stdu_cursor_funcs,1577 vmw_cursor_plane_formats,1578 ARRAY_SIZE(vmw_cursor_plane_formats),1579 NULL, DRM_PLANE_TYPE_CURSOR, NULL);1580 if (ret) {1581 DRM_ERROR("Failed to initialize cursor plane");1582 drm_plane_cleanup(&stdu->base.primary);1583 goto err_free;1584 }1585 1586 drm_plane_helper_add(&cursor->base, &vmw_stdu_cursor_plane_helper_funcs);1587 1588 ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs,1589 DRM_MODE_CONNECTOR_VIRTUAL);1590 if (ret) {1591 DRM_ERROR("Failed to initialize connector\n");1592 goto err_free;1593 }1594 1595 drm_connector_helper_add(connector, &vmw_stdu_connector_helper_funcs);1596 connector->status = vmw_du_connector_detect(connector, false);1597 1598 ret = drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs,1599 DRM_MODE_ENCODER_VIRTUAL, NULL);1600 if (ret) {1601 DRM_ERROR("Failed to initialize encoder\n");1602 goto err_free_connector;1603 }1604 1605 (void) drm_connector_attach_encoder(connector, encoder);1606 encoder->possible_crtcs = (1 << unit);1607 encoder->possible_clones = 0;1608 1609 ret = drm_connector_register(connector);1610 if (ret) {1611 DRM_ERROR("Failed to register connector\n");1612 goto err_free_encoder;1613 }1614 1615 ret = drm_crtc_init_with_planes(dev, crtc, primary,1616 &cursor->base,1617 &vmw_stdu_crtc_funcs, NULL);1618 if (ret) {1619 DRM_ERROR("Failed to initialize CRTC\n");1620 goto err_free_unregister;1621 }1622 1623 drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs);1624 1625 drm_mode_crtc_set_gamma_size(crtc, 256);1626 1627 drm_object_attach_property(&connector->base,1628 dev_priv->hotplug_mode_update_property, 1);1629 drm_object_attach_property(&connector->base,1630 dev->mode_config.suggested_x_property, 0);1631 drm_object_attach_property(&connector->base,1632 dev->mode_config.suggested_y_property, 0);1633 1634 vmw_du_init(&stdu->base);1635 1636 return 0;1637 1638err_free_unregister:1639 drm_connector_unregister(connector);1640err_free_encoder:1641 drm_encoder_cleanup(encoder);1642err_free_connector:1643 drm_connector_cleanup(connector);1644err_free:1645 kfree(stdu);1646 return ret;1647}1648 1649 1650 1651/**1652 * vmw_stdu_destroy - Cleans up a vmw_screen_target_display_unit1653 *1654 * @stdu: Screen Target Display Unit to be destroyed1655 *1656 * Clean up after vmw_stdu_init1657 */1658static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu)1659{1660 vmw_du_cleanup(&stdu->base);1661 kfree(stdu);1662}1663 1664 1665 1666/******************************************************************************1667 * Screen Target Display KMS Functions1668 *1669 * These functions are called by the common KMS code in vmwgfx_kms.c1670 *****************************************************************************/1671 1672/**1673 * vmw_kms_stdu_init_display - Initializes a Screen Target based display1674 *1675 * @dev_priv: VMW DRM device1676 *1677 * This function initialize a Screen Target based display device. It checks1678 * the capability bits to make sure the underlying hardware can support1679 * screen targets, and then creates the maximum number of CRTCs, a.k.a Display1680 * Units, as supported by the display hardware.1681 *1682 * RETURNS:1683 * 0 on success, error code otherwise1684 */1685int vmw_kms_stdu_init_display(struct vmw_private *dev_priv)1686{1687 struct drm_device *dev = &dev_priv->drm;1688 int i, ret;1689 1690 1691 /* Do nothing if there's no support for MOBs */1692 if (!dev_priv->has_mob)1693 return -ENOSYS;1694 1695 if (!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS))1696 return -ENOSYS;1697 1698 dev_priv->active_display_unit = vmw_du_screen_target;1699 1700 for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) {1701 ret = vmw_stdu_init(dev_priv, i);1702 1703 if (unlikely(ret != 0)) {1704 drm_err(&dev_priv->drm,1705 "Failed to initialize STDU %d", i);1706 return ret;1707 }1708 }1709 1710 drm_mode_config_reset(dev);1711 1712 return 0;1713}1714