1367 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2/**************************************************************************3 *4 * Copyright (c) 2011-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 33#include <drm/drm_atomic.h>34#include <drm/drm_atomic_helper.h>35#include <drm/drm_damage_helper.h>36#include <drm/drm_fourcc.h>37#include <drm/drm_vblank.h>38 39#define vmw_crtc_to_sou(x) \40 container_of(x, struct vmw_screen_object_unit, base.crtc)41#define vmw_encoder_to_sou(x) \42 container_of(x, struct vmw_screen_object_unit, base.encoder)43#define vmw_connector_to_sou(x) \44 container_of(x, struct vmw_screen_object_unit, base.connector)45 46/**47 * struct vmw_kms_sou_surface_dirty - Closure structure for48 * blit surface to screen command.49 * @base: The base type we derive from. Used by vmw_kms_helper_dirty().50 * @left: Left side of bounding box.51 * @right: Right side of bounding box.52 * @top: Top side of bounding box.53 * @bottom: Bottom side of bounding box.54 * @dst_x: Difference between source clip rects and framebuffer coordinates.55 * @dst_y: Difference between source clip rects and framebuffer coordinates.56 * @sid: Surface id of surface to copy from.57 */58struct vmw_kms_sou_surface_dirty {59 struct vmw_kms_dirty base;60 s32 left, right, top, bottom;61 s32 dst_x, dst_y;62 u32 sid;63};64 65/*66 * SVGA commands that are used by this code. Please see the device headers67 * for explanation.68 */69struct vmw_kms_sou_readback_blit {70 uint32 header;71 SVGAFifoCmdBlitScreenToGMRFB body;72};73 74struct vmw_kms_sou_bo_blit {75 uint32 header;76 SVGAFifoCmdBlitGMRFBToScreen body;77};78 79struct vmw_kms_sou_dirty_cmd {80 SVGA3dCmdHeader header;81 SVGA3dCmdBlitSurfaceToScreen body;82};83 84struct vmw_kms_sou_define_gmrfb {85 uint32_t header;86 SVGAFifoCmdDefineGMRFB body;87};88 89/*90 * Display unit using screen objects.91 */92struct vmw_screen_object_unit {93 struct vmw_display_unit base;94 95 struct vmw_bo *buffer; /**< Backing store buffer */96 97 bool defined;98};99 100static void vmw_sou_destroy(struct vmw_screen_object_unit *sou)101{102 vmw_du_cleanup(&sou->base);103 kfree(sou);104}105 106 107/*108 * Screen Object Display Unit CRTC functions109 */110 111static void vmw_sou_crtc_destroy(struct drm_crtc *crtc)112{113 vmw_sou_destroy(vmw_crtc_to_sou(crtc));114}115 116/*117 * Send the fifo command to create a screen.118 */119static int vmw_sou_fifo_create(struct vmw_private *dev_priv,120 struct vmw_screen_object_unit *sou,121 int x, int y,122 struct drm_display_mode *mode)123{124 size_t fifo_size;125 126 struct {127 struct {128 uint32_t cmdType;129 } header;130 SVGAScreenObject obj;131 } *cmd;132 133 BUG_ON(!sou->buffer);134 135 fifo_size = sizeof(*cmd);136 cmd = VMW_CMD_RESERVE(dev_priv, fifo_size);137 if (unlikely(cmd == NULL))138 return -ENOMEM;139 140 memset(cmd, 0, fifo_size);141 cmd->header.cmdType = SVGA_CMD_DEFINE_SCREEN;142 cmd->obj.structSize = sizeof(SVGAScreenObject);143 cmd->obj.id = sou->base.unit;144 cmd->obj.flags = SVGA_SCREEN_HAS_ROOT |145 (sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0);146 cmd->obj.size.width = mode->hdisplay;147 cmd->obj.size.height = mode->vdisplay;148 cmd->obj.root.x = x;149 cmd->obj.root.y = y;150 sou->base.set_gui_x = cmd->obj.root.x;151 sou->base.set_gui_y = cmd->obj.root.y;152 153 /* Ok to assume that buffer is pinned in vram */154 vmw_bo_get_guest_ptr(&sou->buffer->tbo, &cmd->obj.backingStore.ptr);155 cmd->obj.backingStore.pitch = mode->hdisplay * 4;156 157 vmw_cmd_commit(dev_priv, fifo_size);158 159 sou->defined = true;160 161 return 0;162}163 164/*165 * Send the fifo command to destroy a screen.166 */167static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv,168 struct vmw_screen_object_unit *sou)169{170 size_t fifo_size;171 int ret;172 173 struct {174 struct {175 uint32_t cmdType;176 } header;177 SVGAFifoCmdDestroyScreen body;178 } *cmd;179 180 /* no need to do anything */181 if (unlikely(!sou->defined))182 return 0;183 184 fifo_size = sizeof(*cmd);185 cmd = VMW_CMD_RESERVE(dev_priv, fifo_size);186 if (unlikely(cmd == NULL))187 return -ENOMEM;188 189 memset(cmd, 0, fifo_size);190 cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN;191 cmd->body.screenId = sou->base.unit;192 193 vmw_cmd_commit(dev_priv, fifo_size);194 195 /* Force sync */196 ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ);197 if (unlikely(ret != 0))198 DRM_ERROR("Failed to sync with HW");199 else200 sou->defined = false;201 202 return ret;203}204 205/**206 * vmw_sou_crtc_mode_set_nofb - Create new screen207 *208 * @crtc: CRTC associated with the new screen209 *210 * This function creates/destroys a screen. This function cannot fail, so if211 * somehow we run into a failure, just do the best we can to get out.212 */213static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc)214{215 struct vmw_private *dev_priv;216 struct vmw_screen_object_unit *sou;217 struct vmw_framebuffer *vfb;218 struct drm_framebuffer *fb;219 struct drm_plane_state *ps;220 struct vmw_plane_state *vps;221 int ret;222 223 sou = vmw_crtc_to_sou(crtc);224 dev_priv = vmw_priv(crtc->dev);225 ps = crtc->primary->state;226 fb = ps->fb;227 vps = vmw_plane_state_to_vps(ps);228 229 vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;230 231 if (sou->defined) {232 ret = vmw_sou_fifo_destroy(dev_priv, sou);233 if (ret) {234 DRM_ERROR("Failed to destroy Screen Object\n");235 return;236 }237 }238 239 if (vfb) {240 struct drm_connector_state *conn_state;241 struct vmw_connector_state *vmw_conn_state;242 int x, y;243 244 sou->buffer = vmw_user_object_buffer(&vps->uo);245 246 conn_state = sou->base.connector.state;247 vmw_conn_state = vmw_connector_state_to_vcs(conn_state);248 249 x = vmw_conn_state->gui_x;250 y = vmw_conn_state->gui_y;251 252 ret = vmw_sou_fifo_create(dev_priv, sou, x, y, &crtc->mode);253 if (ret)254 DRM_ERROR("Failed to define Screen Object %dx%d\n",255 crtc->x, crtc->y);256 257 } else {258 sou->buffer = NULL;259 }260}261 262/**263 * vmw_sou_crtc_helper_prepare - Noop264 *265 * @crtc: CRTC associated with the new screen266 *267 * Prepares the CRTC for a mode set, but we don't need to do anything here.268 */269static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc)270{271}272 273/**274 * vmw_sou_crtc_atomic_disable - Turns off CRTC275 *276 * @crtc: CRTC to be turned off277 * @state: Unused278 */279static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc,280 struct drm_atomic_state *state)281{282 struct vmw_private *dev_priv;283 struct vmw_screen_object_unit *sou;284 int ret;285 286 287 if (!crtc) {288 DRM_ERROR("CRTC is NULL\n");289 return;290 }291 292 sou = vmw_crtc_to_sou(crtc);293 dev_priv = vmw_priv(crtc->dev);294 295 if (dev_priv->vkms_enabled)296 drm_crtc_vblank_off(crtc);297 298 if (sou->defined) {299 ret = vmw_sou_fifo_destroy(dev_priv, sou);300 if (ret)301 DRM_ERROR("Failed to destroy Screen Object\n");302 }303}304 305static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {306 .gamma_set = vmw_du_crtc_gamma_set,307 .destroy = vmw_sou_crtc_destroy,308 .reset = vmw_du_crtc_reset,309 .atomic_duplicate_state = vmw_du_crtc_duplicate_state,310 .atomic_destroy_state = vmw_du_crtc_destroy_state,311 .set_config = drm_atomic_helper_set_config,312 .page_flip = drm_atomic_helper_page_flip,313 .enable_vblank = vmw_vkms_enable_vblank,314 .disable_vblank = vmw_vkms_disable_vblank,315 .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,316};317 318/*319 * Screen Object Display Unit encoder functions320 */321 322static void vmw_sou_encoder_destroy(struct drm_encoder *encoder)323{324 vmw_sou_destroy(vmw_encoder_to_sou(encoder));325}326 327static const struct drm_encoder_funcs vmw_screen_object_encoder_funcs = {328 .destroy = vmw_sou_encoder_destroy,329};330 331/*332 * Screen Object Display Unit connector functions333 */334 335static void vmw_sou_connector_destroy(struct drm_connector *connector)336{337 vmw_sou_destroy(vmw_connector_to_sou(connector));338}339 340static const struct drm_connector_funcs vmw_sou_connector_funcs = {341 .dpms = vmw_du_connector_dpms,342 .detect = vmw_du_connector_detect,343 .fill_modes = drm_helper_probe_single_connector_modes,344 .destroy = vmw_sou_connector_destroy,345 .reset = vmw_du_connector_reset,346 .atomic_duplicate_state = vmw_du_connector_duplicate_state,347 .atomic_destroy_state = vmw_du_connector_destroy_state,348};349 350 351static const struct352drm_connector_helper_funcs vmw_sou_connector_helper_funcs = {353 .get_modes = vmw_connector_get_modes,354 .mode_valid = vmw_connector_mode_valid355};356 357 358 359/*360 * Screen Object Display Plane Functions361 */362 363/**364 * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer365 *366 * @plane: display plane367 * @old_state: Contains the FB to clean up368 *369 * Unpins the display surface370 *371 * Returns 0 on success372 */373static void374vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,375 struct drm_plane_state *old_state)376{377 struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);378 struct drm_crtc *crtc = plane->state->crtc ?379 plane->state->crtc : old_state->crtc;380 struct vmw_bo *bo = vmw_user_object_buffer(&vps->uo);381 382 if (bo)383 vmw_bo_unpin(vmw_priv(crtc->dev), bo, false);384 vmw_user_object_unref(&vps->uo);385 vps->bo_size = 0;386 387 vmw_du_plane_cleanup_fb(plane, old_state);388}389 390 391/**392 * vmw_sou_primary_plane_prepare_fb - allocate backing buffer393 *394 * @plane: display plane395 * @new_state: info on the new plane state, including the FB396 *397 * The SOU backing buffer is our equivalent of the display plane.398 *399 * Returns 0 on success400 */401static int402vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,403 struct drm_plane_state *new_state)404{405 struct drm_framebuffer *new_fb = new_state->fb;406 struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc;407 struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);408 struct vmw_private *dev_priv;409 int ret;410 struct vmw_bo_params bo_params = {411 .domain = VMW_BO_DOMAIN_VRAM,412 .busy_domain = VMW_BO_DOMAIN_VRAM,413 .bo_type = ttm_bo_type_device,414 .pin = true415 };416 struct vmw_bo *bo = NULL;417 418 if (!new_fb) {419 vmw_user_object_unref(&vps->uo);420 vps->bo_size = 0;421 422 return 0;423 }424 425 bo_params.size = new_state->crtc_w * new_state->crtc_h * 4;426 dev_priv = vmw_priv(crtc->dev);427 428 bo = vmw_user_object_buffer(&vps->uo);429 if (bo) {430 if (vps->bo_size == bo_params.size) {431 /*432 * Note that this might temporarily up the pin-count433 * to 2, until cleanup_fb() is called.434 */435 return vmw_bo_pin_in_vram(dev_priv, bo, true);436 }437 438 vmw_user_object_unref(&vps->uo);439 vps->bo_size = 0;440 }441 442 vmw_svga_enable(dev_priv);443 444 /* After we have alloced the backing store might not be able to445 * resume the overlays, this is preferred to failing to alloc.446 */447 vmw_overlay_pause_all(dev_priv);448 ret = vmw_gem_object_create(dev_priv, &bo_params, &vps->uo.buffer);449 vmw_overlay_resume_all(dev_priv);450 if (ret)451 return ret;452 453 vps->bo_size = bo_params.size;454 455 /*456 * TTM already thinks the buffer is pinned, but make sure the457 * pin_count is upped.458 */459 return vmw_bo_pin_in_vram(dev_priv, vps->uo.buffer, true);460}461 462static uint32_t vmw_sou_bo_fifo_size(struct vmw_du_update_plane *update,463 uint32_t num_hits)464{465 return sizeof(struct vmw_kms_sou_define_gmrfb) +466 sizeof(struct vmw_kms_sou_bo_blit) * num_hits;467}468 469static uint32_t vmw_sou_bo_define_gmrfb(struct vmw_du_update_plane *update,470 void *cmd)471{472 struct vmw_framebuffer_bo *vfbbo =473 container_of(update->vfb, typeof(*vfbbo), base);474 struct vmw_kms_sou_define_gmrfb *gmr = cmd;475 int depth = update->vfb->base.format->depth;476 477 /* Emulate RGBA support, contrary to svga_reg.h this is not478 * supported by hosts. This is only a problem if we are reading479 * this value later and expecting what we uploaded back.480 */481 if (depth == 32)482 depth = 24;483 484 gmr->header = SVGA_CMD_DEFINE_GMRFB;485 486 gmr->body.format.bitsPerPixel = update->vfb->base.format->cpp[0] * 8;487 gmr->body.format.colorDepth = depth;488 gmr->body.format.reserved = 0;489 gmr->body.bytesPerLine = update->vfb->base.pitches[0];490 vmw_bo_get_guest_ptr(&vfbbo->buffer->tbo, &gmr->body.ptr);491 492 return sizeof(*gmr);493}494 495static uint32_t vmw_sou_bo_populate_clip(struct vmw_du_update_plane *update,496 void *cmd, struct drm_rect *clip,497 uint32_t fb_x, uint32_t fb_y)498{499 struct vmw_kms_sou_bo_blit *blit = cmd;500 501 blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN;502 blit->body.destScreenId = update->du->unit;503 blit->body.srcOrigin.x = fb_x;504 blit->body.srcOrigin.y = fb_y;505 blit->body.destRect.left = clip->x1;506 blit->body.destRect.top = clip->y1;507 blit->body.destRect.right = clip->x2;508 blit->body.destRect.bottom = clip->y2;509 510 return sizeof(*blit);511}512 513static uint32_t vmw_stud_bo_post_clip(struct vmw_du_update_plane *update,514 void *cmd, struct drm_rect *bb)515{516 return 0;517}518 519/**520 * vmw_sou_plane_update_bo - Update display unit for bo backed fb.521 * @dev_priv: Device private.522 * @plane: Plane state.523 * @old_state: Old plane state.524 * @vfb: Framebuffer which is blitted to display unit.525 * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj.526 * The returned fence pointer may be NULL in which case the device527 * has already synchronized.528 *529 * Return: 0 on success or a negative error code on failure.530 */531static int vmw_sou_plane_update_bo(struct vmw_private *dev_priv,532 struct drm_plane *plane,533 struct drm_plane_state *old_state,534 struct vmw_framebuffer *vfb,535 struct vmw_fence_obj **out_fence)536{537 struct vmw_du_update_plane_buffer bo_update;538 539 memset(&bo_update, 0, sizeof(struct vmw_du_update_plane_buffer));540 bo_update.base.plane = plane;541 bo_update.base.old_state = old_state;542 bo_update.base.dev_priv = dev_priv;543 bo_update.base.du = vmw_crtc_to_du(plane->state->crtc);544 bo_update.base.vfb = vfb;545 bo_update.base.out_fence = out_fence;546 bo_update.base.mutex = NULL;547 bo_update.base.intr = true;548 549 bo_update.base.calc_fifo_size = vmw_sou_bo_fifo_size;550 bo_update.base.post_prepare = vmw_sou_bo_define_gmrfb;551 bo_update.base.clip = vmw_sou_bo_populate_clip;552 bo_update.base.post_clip = vmw_stud_bo_post_clip;553 554 return vmw_du_helper_plane_update(&bo_update.base);555}556 557static uint32_t vmw_sou_surface_fifo_size(struct vmw_du_update_plane *update,558 uint32_t num_hits)559{560 return sizeof(struct vmw_kms_sou_dirty_cmd) + sizeof(SVGASignedRect) *561 num_hits;562}563 564static uint32_t vmw_sou_surface_post_prepare(struct vmw_du_update_plane *update,565 void *cmd)566{567 struct vmw_du_update_plane_surface *srf_update;568 569 srf_update = container_of(update, typeof(*srf_update), base);570 571 /*572 * SOU SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN is special in the sense that573 * its bounding box is filled before iterating over all the clips. So574 * store the FIFO start address and revisit to fill the details.575 */576 srf_update->cmd_start = cmd;577 578 return 0;579}580 581static uint32_t vmw_sou_surface_pre_clip(struct vmw_du_update_plane *update,582 void *cmd, uint32_t num_hits)583{584 struct vmw_kms_sou_dirty_cmd *blit = cmd;585 struct vmw_framebuffer_surface *vfbs;586 struct vmw_surface *surf = NULL;587 588 vfbs = container_of(update->vfb, typeof(*vfbs), base);589 590 blit->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN;591 blit->header.size = sizeof(blit->body) + sizeof(SVGASignedRect) *592 num_hits;593 594 surf = vmw_user_object_surface(&vfbs->uo);595 blit->body.srcImage.sid = surf->res.id;596 blit->body.destScreenId = update->du->unit;597 598 /* Update the source and destination bounding box later in post_clip */599 blit->body.srcRect.left = 0;600 blit->body.srcRect.top = 0;601 blit->body.srcRect.right = 0;602 blit->body.srcRect.bottom = 0;603 604 blit->body.destRect.left = 0;605 blit->body.destRect.top = 0;606 blit->body.destRect.right = 0;607 blit->body.destRect.bottom = 0;608 609 return sizeof(*blit);610}611 612static uint32_t vmw_sou_surface_clip_rect(struct vmw_du_update_plane *update,613 void *cmd, struct drm_rect *clip,614 uint32_t src_x, uint32_t src_y)615{616 SVGASignedRect *rect = cmd;617 618 /*619 * rects are relative to dest bounding box rect on screen object, so620 * translate to it later in post_clip621 */622 rect->left = clip->x1;623 rect->top = clip->y1;624 rect->right = clip->x2;625 rect->bottom = clip->y2;626 627 return sizeof(*rect);628}629 630static uint32_t vmw_sou_surface_post_clip(struct vmw_du_update_plane *update,631 void *cmd, struct drm_rect *bb)632{633 struct vmw_du_update_plane_surface *srf_update;634 struct drm_plane_state *state = update->plane->state;635 struct drm_rect src_bb;636 struct vmw_kms_sou_dirty_cmd *blit;637 SVGASignedRect *rect;638 uint32_t num_hits;639 int translate_src_x;640 int translate_src_y;641 int i;642 643 srf_update = container_of(update, typeof(*srf_update), base);644 645 blit = srf_update->cmd_start;646 rect = (SVGASignedRect *)&blit[1];647 648 num_hits = (blit->header.size - sizeof(blit->body))/649 sizeof(SVGASignedRect);650 651 src_bb = *bb;652 653 /* To translate bb back to fb src coord */654 translate_src_x = (state->src_x >> 16) - state->crtc_x;655 translate_src_y = (state->src_y >> 16) - state->crtc_y;656 657 drm_rect_translate(&src_bb, translate_src_x, translate_src_y);658 659 blit->body.srcRect.left = src_bb.x1;660 blit->body.srcRect.top = src_bb.y1;661 blit->body.srcRect.right = src_bb.x2;662 blit->body.srcRect.bottom = src_bb.y2;663 664 blit->body.destRect.left = bb->x1;665 blit->body.destRect.top = bb->y1;666 blit->body.destRect.right = bb->x2;667 blit->body.destRect.bottom = bb->y2;668 669 /* rects are relative to dest bb rect */670 for (i = 0; i < num_hits; i++) {671 rect->left -= bb->x1;672 rect->top -= bb->y1;673 rect->right -= bb->x1;674 rect->bottom -= bb->y1;675 rect++;676 }677 678 return 0;679}680 681/**682 * vmw_sou_plane_update_surface - Update display unit for surface backed fb.683 * @dev_priv: Device private.684 * @plane: Plane state.685 * @old_state: Old plane state.686 * @vfb: Framebuffer which is blitted to display unit687 * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj.688 * The returned fence pointer may be NULL in which case the device689 * has already synchronized.690 *691 * Return: 0 on success or a negative error code on failure.692 */693static int vmw_sou_plane_update_surface(struct vmw_private *dev_priv,694 struct drm_plane *plane,695 struct drm_plane_state *old_state,696 struct vmw_framebuffer *vfb,697 struct vmw_fence_obj **out_fence)698{699 struct vmw_du_update_plane_surface srf_update;700 701 memset(&srf_update, 0, sizeof(struct vmw_du_update_plane_surface));702 srf_update.base.plane = plane;703 srf_update.base.old_state = old_state;704 srf_update.base.dev_priv = dev_priv;705 srf_update.base.du = vmw_crtc_to_du(plane->state->crtc);706 srf_update.base.vfb = vfb;707 srf_update.base.out_fence = out_fence;708 srf_update.base.mutex = &dev_priv->cmdbuf_mutex;709 srf_update.base.intr = true;710 711 srf_update.base.calc_fifo_size = vmw_sou_surface_fifo_size;712 srf_update.base.post_prepare = vmw_sou_surface_post_prepare;713 srf_update.base.pre_clip = vmw_sou_surface_pre_clip;714 srf_update.base.clip = vmw_sou_surface_clip_rect;715 srf_update.base.post_clip = vmw_sou_surface_post_clip;716 717 return vmw_du_helper_plane_update(&srf_update.base);718}719 720static void721vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,722 struct drm_atomic_state *state)723{724 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);725 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);726 struct drm_crtc *crtc = new_state->crtc;727 struct vmw_fence_obj *fence = NULL;728 int ret;729 730 /* In case of device error, maintain consistent atomic state */731 if (crtc && new_state->fb) {732 struct vmw_private *dev_priv = vmw_priv(crtc->dev);733 struct vmw_framebuffer *vfb =734 vmw_framebuffer_to_vfb(new_state->fb);735 736 if (vfb->bo)737 ret = vmw_sou_plane_update_bo(dev_priv, plane,738 old_state, vfb, &fence);739 else740 ret = vmw_sou_plane_update_surface(dev_priv, plane,741 old_state, vfb,742 &fence);743 if (ret != 0)744 DRM_ERROR("Failed to update screen.\n");745 } else {746 /* Do nothing when fb and crtc is NULL (blank crtc) */747 return;748 }749 750 if (fence)751 vmw_fence_obj_unreference(&fence);752}753 754 755static const struct drm_plane_funcs vmw_sou_plane_funcs = {756 .update_plane = drm_atomic_helper_update_plane,757 .disable_plane = drm_atomic_helper_disable_plane,758 .destroy = vmw_du_primary_plane_destroy,759 .reset = vmw_du_plane_reset,760 .atomic_duplicate_state = vmw_du_plane_duplicate_state,761 .atomic_destroy_state = vmw_du_plane_destroy_state,762};763 764static const struct drm_plane_funcs vmw_sou_cursor_funcs = {765 .update_plane = drm_atomic_helper_update_plane,766 .disable_plane = drm_atomic_helper_disable_plane,767 .destroy = vmw_du_cursor_plane_destroy,768 .reset = vmw_du_plane_reset,769 .atomic_duplicate_state = vmw_du_plane_duplicate_state,770 .atomic_destroy_state = vmw_du_plane_destroy_state,771};772 773/*774 * Atomic Helpers775 */776static const struct777drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = {778 .atomic_check = vmw_du_cursor_plane_atomic_check,779 .atomic_update = vmw_du_cursor_plane_atomic_update,780 .prepare_fb = vmw_du_cursor_plane_prepare_fb,781 .cleanup_fb = vmw_du_cursor_plane_cleanup_fb,782};783 784static const struct785drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = {786 .atomic_check = vmw_du_primary_plane_atomic_check,787 .atomic_update = vmw_sou_primary_plane_atomic_update,788 .prepare_fb = vmw_sou_primary_plane_prepare_fb,789 .cleanup_fb = vmw_sou_primary_plane_cleanup_fb,790};791 792static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {793 .prepare = vmw_sou_crtc_helper_prepare,794 .mode_set_nofb = vmw_sou_crtc_mode_set_nofb,795 .atomic_check = vmw_du_crtc_atomic_check,796 .atomic_begin = vmw_du_crtc_atomic_begin,797 .atomic_flush = vmw_vkms_crtc_atomic_flush,798 .atomic_enable = vmw_vkms_crtc_atomic_enable,799 .atomic_disable = vmw_sou_crtc_atomic_disable,800};801 802 803static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)804{805 struct vmw_screen_object_unit *sou;806 struct drm_device *dev = &dev_priv->drm;807 struct drm_connector *connector;808 struct drm_encoder *encoder;809 struct drm_plane *primary;810 struct vmw_cursor_plane *cursor;811 struct drm_crtc *crtc;812 int ret;813 814 sou = kzalloc(sizeof(*sou), GFP_KERNEL);815 if (!sou)816 return -ENOMEM;817 818 sou->base.unit = unit;819 crtc = &sou->base.crtc;820 encoder = &sou->base.encoder;821 connector = &sou->base.connector;822 primary = &sou->base.primary;823 cursor = &sou->base.cursor;824 825 sou->base.pref_active = (unit == 0);826 sou->base.pref_width = dev_priv->initial_width;827 sou->base.pref_height = dev_priv->initial_height;828 829 /*830 * Remove this after enabling atomic because property values can831 * only exist in a state object832 */833 sou->base.is_implicit = false;834 835 /* Initialize primary plane */836 ret = drm_universal_plane_init(dev, primary,837 0, &vmw_sou_plane_funcs,838 vmw_primary_plane_formats,839 ARRAY_SIZE(vmw_primary_plane_formats),840 NULL, DRM_PLANE_TYPE_PRIMARY, NULL);841 if (ret) {842 DRM_ERROR("Failed to initialize primary plane");843 goto err_free;844 }845 846 drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs);847 drm_plane_enable_fb_damage_clips(primary);848 849 /* Initialize cursor plane */850 ret = drm_universal_plane_init(dev, &cursor->base,851 0, &vmw_sou_cursor_funcs,852 vmw_cursor_plane_formats,853 ARRAY_SIZE(vmw_cursor_plane_formats),854 NULL, DRM_PLANE_TYPE_CURSOR, NULL);855 if (ret) {856 DRM_ERROR("Failed to initialize cursor plane");857 drm_plane_cleanup(&sou->base.primary);858 goto err_free;859 }860 861 drm_plane_helper_add(&cursor->base, &vmw_sou_cursor_plane_helper_funcs);862 863 ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs,864 DRM_MODE_CONNECTOR_VIRTUAL);865 if (ret) {866 DRM_ERROR("Failed to initialize connector\n");867 goto err_free;868 }869 870 drm_connector_helper_add(connector, &vmw_sou_connector_helper_funcs);871 connector->status = vmw_du_connector_detect(connector, true);872 873 ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs,874 DRM_MODE_ENCODER_VIRTUAL, NULL);875 if (ret) {876 DRM_ERROR("Failed to initialize encoder\n");877 goto err_free_connector;878 }879 880 (void) drm_connector_attach_encoder(connector, encoder);881 encoder->possible_crtcs = (1 << unit);882 encoder->possible_clones = 0;883 884 ret = drm_connector_register(connector);885 if (ret) {886 DRM_ERROR("Failed to register connector\n");887 goto err_free_encoder;888 }889 890 ret = drm_crtc_init_with_planes(dev, crtc, primary,891 &cursor->base,892 &vmw_screen_object_crtc_funcs, NULL);893 if (ret) {894 DRM_ERROR("Failed to initialize CRTC\n");895 goto err_free_unregister;896 }897 898 drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs);899 900 drm_mode_crtc_set_gamma_size(crtc, 256);901 902 drm_object_attach_property(&connector->base,903 dev_priv->hotplug_mode_update_property, 1);904 drm_object_attach_property(&connector->base,905 dev->mode_config.suggested_x_property, 0);906 drm_object_attach_property(&connector->base,907 dev->mode_config.suggested_y_property, 0);908 909 vmw_du_init(&sou->base);910 911 return 0;912 913err_free_unregister:914 drm_connector_unregister(connector);915err_free_encoder:916 drm_encoder_cleanup(encoder);917err_free_connector:918 drm_connector_cleanup(connector);919err_free:920 kfree(sou);921 return ret;922}923 924int vmw_kms_sou_init_display(struct vmw_private *dev_priv)925{926 struct drm_device *dev = &dev_priv->drm;927 int i;928 929 /* Screen objects won't work if GMR's aren't available */930 if (!dev_priv->has_gmr)931 return -ENOSYS;932 933 if (!(dev_priv->capabilities & SVGA_CAP_SCREEN_OBJECT_2)) {934 return -ENOSYS;935 }936 937 for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)938 vmw_sou_init(dev_priv, i);939 940 dev_priv->active_display_unit = vmw_du_screen_object;941 942 drm_mode_config_reset(dev);943 944 return 0;945}946 947static int do_bo_define_gmrfb(struct vmw_private *dev_priv,948 struct vmw_framebuffer *framebuffer)949{950 struct vmw_bo *buf =951 container_of(framebuffer, struct vmw_framebuffer_bo,952 base)->buffer;953 int depth = framebuffer->base.format->depth;954 struct {955 uint32_t header;956 SVGAFifoCmdDefineGMRFB body;957 } *cmd;958 959 /* Emulate RGBA support, contrary to svga_reg.h this is not960 * supported by hosts. This is only a problem if we are reading961 * this value later and expecting what we uploaded back.962 */963 if (depth == 32)964 depth = 24;965 966 cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd));967 if (!cmd)968 return -ENOMEM;969 970 cmd->header = SVGA_CMD_DEFINE_GMRFB;971 cmd->body.format.bitsPerPixel = framebuffer->base.format->cpp[0] * 8;972 cmd->body.format.colorDepth = depth;973 cmd->body.format.reserved = 0;974 cmd->body.bytesPerLine = framebuffer->base.pitches[0];975 /* Buffer is reserved in vram or GMR */976 vmw_bo_get_guest_ptr(&buf->tbo, &cmd->body.ptr);977 vmw_cmd_commit(dev_priv, sizeof(*cmd));978 979 return 0;980}981 982/**983 * vmw_sou_surface_fifo_commit - Callback to fill in and submit a984 * blit surface to screen command.985 *986 * @dirty: The closure structure.987 *988 * Fills in the missing fields in the command, and translates the cliprects989 * to match the destination bounding box encoded.990 */991static void vmw_sou_surface_fifo_commit(struct vmw_kms_dirty *dirty)992{993 struct vmw_kms_sou_surface_dirty *sdirty =994 container_of(dirty, typeof(*sdirty), base);995 struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd;996 s32 trans_x = dirty->unit->crtc.x - sdirty->dst_x;997 s32 trans_y = dirty->unit->crtc.y - sdirty->dst_y;998 size_t region_size = dirty->num_hits * sizeof(SVGASignedRect);999 SVGASignedRect *blit = (SVGASignedRect *) &cmd[1];1000 int i;1001 1002 if (!dirty->num_hits) {1003 vmw_cmd_commit(dirty->dev_priv, 0);1004 return;1005 }1006 1007 cmd->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN;1008 cmd->header.size = sizeof(cmd->body) + region_size;1009 1010 /*1011 * Use the destination bounding box to specify destination - and1012 * source bounding regions.1013 */1014 cmd->body.destRect.left = sdirty->left;1015 cmd->body.destRect.right = sdirty->right;1016 cmd->body.destRect.top = sdirty->top;1017 cmd->body.destRect.bottom = sdirty->bottom;1018 1019 cmd->body.srcRect.left = sdirty->left + trans_x;1020 cmd->body.srcRect.right = sdirty->right + trans_x;1021 cmd->body.srcRect.top = sdirty->top + trans_y;1022 cmd->body.srcRect.bottom = sdirty->bottom + trans_y;1023 1024 cmd->body.srcImage.sid = sdirty->sid;1025 cmd->body.destScreenId = dirty->unit->unit;1026 1027 /* Blits are relative to the destination rect. Translate. */1028 for (i = 0; i < dirty->num_hits; ++i, ++blit) {1029 blit->left -= sdirty->left;1030 blit->right -= sdirty->left;1031 blit->top -= sdirty->top;1032 blit->bottom -= sdirty->top;1033 }1034 1035 vmw_cmd_commit(dirty->dev_priv, region_size + sizeof(*cmd));1036 1037 sdirty->left = sdirty->top = S32_MAX;1038 sdirty->right = sdirty->bottom = S32_MIN;1039}1040 1041/**1042 * vmw_sou_surface_clip - Callback to encode a blit surface to screen cliprect.1043 *1044 * @dirty: The closure structure1045 *1046 * Encodes a SVGASignedRect cliprect and updates the bounding box of the1047 * BLIT_SURFACE_TO_SCREEN command.1048 */1049static void vmw_sou_surface_clip(struct vmw_kms_dirty *dirty)1050{1051 struct vmw_kms_sou_surface_dirty *sdirty =1052 container_of(dirty, typeof(*sdirty), base);1053 struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd;1054 SVGASignedRect *blit = (SVGASignedRect *) &cmd[1];1055 1056 /* Destination rect. */1057 blit += dirty->num_hits;1058 blit->left = dirty->unit_x1;1059 blit->top = dirty->unit_y1;1060 blit->right = dirty->unit_x2;1061 blit->bottom = dirty->unit_y2;1062 1063 /* Destination bounding box */1064 sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1);1065 sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1);1066 sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2);1067 sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2);1068 1069 dirty->num_hits++;1070}1071 1072/**1073 * vmw_kms_sou_do_surface_dirty - Dirty part of a surface backed framebuffer1074 *1075 * @dev_priv: Pointer to the device private structure.1076 * @framebuffer: Pointer to the surface-buffer backed framebuffer.1077 * @clips: Array of clip rects. Either @clips or @vclips must be NULL.1078 * @vclips: Alternate array of clip rects. Either @clips or @vclips must1079 * be NULL.1080 * @srf: Pointer to surface to blit from. If NULL, the surface attached1081 * to @framebuffer will be used.1082 * @dest_x: X coordinate offset to align @srf with framebuffer coordinates.1083 * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates.1084 * @num_clips: Number of clip rects in @clips.1085 * @inc: Increment to use when looping over @clips.1086 * @out_fence: If non-NULL, will return a ref-counted pointer to a1087 * struct vmw_fence_obj. The returned fence pointer may be NULL in which1088 * case the device has already synchronized.1089 * @crtc: If crtc is passed, perform surface dirty on that crtc only.1090 *1091 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if1092 * interrupted.1093 */1094int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv,1095 struct vmw_framebuffer *framebuffer,1096 struct drm_clip_rect *clips,1097 struct drm_vmw_rect *vclips,1098 struct vmw_resource *srf,1099 s32 dest_x,1100 s32 dest_y,1101 unsigned num_clips, int inc,1102 struct vmw_fence_obj **out_fence,1103 struct drm_crtc *crtc)1104{1105 struct vmw_framebuffer_surface *vfbs =1106 container_of(framebuffer, typeof(*vfbs), base);1107 struct vmw_kms_sou_surface_dirty sdirty;1108 DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);1109 int ret;1110 1111 if (!srf)1112 srf = &vmw_user_object_surface(&vfbs->uo)->res;1113 1114 ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE,1115 NULL, NULL);1116 if (ret)1117 return ret;1118 1119 ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true);1120 if (ret)1121 goto out_unref;1122 1123 sdirty.base.fifo_commit = vmw_sou_surface_fifo_commit;1124 sdirty.base.clip = vmw_sou_surface_clip;1125 sdirty.base.dev_priv = dev_priv;1126 sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) +1127 sizeof(SVGASignedRect) * num_clips;1128 sdirty.base.crtc = crtc;1129 1130 sdirty.sid = srf->id;1131 sdirty.left = sdirty.top = S32_MAX;1132 sdirty.right = sdirty.bottom = S32_MIN;1133 sdirty.dst_x = dest_x;1134 sdirty.dst_y = dest_y;1135 1136 ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,1137 dest_x, dest_y, num_clips, inc,1138 &sdirty.base);1139 vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence,1140 NULL);1141 1142 return ret;1143 1144out_unref:1145 vmw_validation_unref_lists(&val_ctx);1146 return ret;1147}1148 1149/**1150 * vmw_sou_bo_fifo_commit - Callback to submit a set of readback clips.1151 *1152 * @dirty: The closure structure.1153 *1154 * Commits a previously built command buffer of readback clips.1155 */1156static void vmw_sou_bo_fifo_commit(struct vmw_kms_dirty *dirty)1157{1158 if (!dirty->num_hits) {1159 vmw_cmd_commit(dirty->dev_priv, 0);1160 return;1161 }1162 1163 vmw_cmd_commit(dirty->dev_priv,1164 sizeof(struct vmw_kms_sou_bo_blit) *1165 dirty->num_hits);1166}1167 1168/**1169 * vmw_sou_bo_clip - Callback to encode a readback cliprect.1170 *1171 * @dirty: The closure structure1172 *1173 * Encodes a BLIT_GMRFB_TO_SCREEN cliprect.1174 */1175static void vmw_sou_bo_clip(struct vmw_kms_dirty *dirty)1176{1177 struct vmw_kms_sou_bo_blit *blit = dirty->cmd;1178 1179 blit += dirty->num_hits;1180 blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN;1181 blit->body.destScreenId = dirty->unit->unit;1182 blit->body.srcOrigin.x = dirty->fb_x;1183 blit->body.srcOrigin.y = dirty->fb_y;1184 blit->body.destRect.left = dirty->unit_x1;1185 blit->body.destRect.top = dirty->unit_y1;1186 blit->body.destRect.right = dirty->unit_x2;1187 blit->body.destRect.bottom = dirty->unit_y2;1188 dirty->num_hits++;1189}1190 1191/**1192 * vmw_kms_sou_do_bo_dirty - Dirty part of a buffer-object backed framebuffer1193 *1194 * @dev_priv: Pointer to the device private structure.1195 * @framebuffer: Pointer to the buffer-object backed framebuffer.1196 * @clips: Array of clip rects.1197 * @vclips: Alternate array of clip rects. Either @clips or @vclips must1198 * be NULL.1199 * @num_clips: Number of clip rects in @clips.1200 * @increment: Increment to use when looping over @clips.1201 * @interruptible: Whether to perform waits interruptible if possible.1202 * @out_fence: If non-NULL, will return a ref-counted pointer to a1203 * struct vmw_fence_obj. The returned fence pointer may be NULL in which1204 * case the device has already synchronized.1205 * @crtc: If crtc is passed, perform bo dirty on that crtc only.1206 *1207 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if1208 * interrupted.1209 */1210int vmw_kms_sou_do_bo_dirty(struct vmw_private *dev_priv,1211 struct vmw_framebuffer *framebuffer,1212 struct drm_clip_rect *clips,1213 struct drm_vmw_rect *vclips,1214 unsigned num_clips, int increment,1215 bool interruptible,1216 struct vmw_fence_obj **out_fence,1217 struct drm_crtc *crtc)1218{1219 struct vmw_bo *buf =1220 container_of(framebuffer, struct vmw_framebuffer_bo,1221 base)->buffer;1222 struct vmw_kms_dirty dirty;1223 DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);1224 int ret;1225 1226 vmw_bo_placement_set(buf, VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM,1227 VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM);1228 ret = vmw_validation_add_bo(&val_ctx, buf);1229 if (ret)1230 return ret;1231 1232 ret = vmw_validation_prepare(&val_ctx, NULL, interruptible);1233 if (ret)1234 goto out_unref;1235 1236 ret = do_bo_define_gmrfb(dev_priv, framebuffer);1237 if (unlikely(ret != 0))1238 goto out_revert;1239 1240 dirty.crtc = crtc;1241 dirty.fifo_commit = vmw_sou_bo_fifo_commit;1242 dirty.clip = vmw_sou_bo_clip;1243 dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_bo_blit) *1244 num_clips;1245 ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,1246 0, 0, num_clips, increment, &dirty);1247 vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence,1248 NULL);1249 1250 return ret;1251 1252out_revert:1253 vmw_validation_revert(&val_ctx);1254out_unref:1255 vmw_validation_unref_lists(&val_ctx);1256 1257 return ret;1258}1259 1260 1261/**1262 * vmw_sou_readback_fifo_commit - Callback to submit a set of readback clips.1263 *1264 * @dirty: The closure structure.1265 *1266 * Commits a previously built command buffer of readback clips.1267 */1268static void vmw_sou_readback_fifo_commit(struct vmw_kms_dirty *dirty)1269{1270 if (!dirty->num_hits) {1271 vmw_cmd_commit(dirty->dev_priv, 0);1272 return;1273 }1274 1275 vmw_cmd_commit(dirty->dev_priv,1276 sizeof(struct vmw_kms_sou_readback_blit) *1277 dirty->num_hits);1278}1279 1280/**1281 * vmw_sou_readback_clip - Callback to encode a readback cliprect.1282 *1283 * @dirty: The closure structure1284 *1285 * Encodes a BLIT_SCREEN_TO_GMRFB cliprect.1286 */1287static void vmw_sou_readback_clip(struct vmw_kms_dirty *dirty)1288{1289 struct vmw_kms_sou_readback_blit *blit = dirty->cmd;1290 1291 blit += dirty->num_hits;1292 blit->header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB;1293 blit->body.srcScreenId = dirty->unit->unit;1294 blit->body.destOrigin.x = dirty->fb_x;1295 blit->body.destOrigin.y = dirty->fb_y;1296 blit->body.srcRect.left = dirty->unit_x1;1297 blit->body.srcRect.top = dirty->unit_y1;1298 blit->body.srcRect.right = dirty->unit_x2;1299 blit->body.srcRect.bottom = dirty->unit_y2;1300 dirty->num_hits++;1301}1302 1303/**1304 * vmw_kms_sou_readback - Perform a readback from the screen object system to1305 * a buffer-object backed framebuffer.1306 *1307 * @dev_priv: Pointer to the device private structure.1308 * @file_priv: Pointer to a struct drm_file identifying the caller.1309 * Must be set to NULL if @user_fence_rep is NULL.1310 * @vfb: Pointer to the buffer-object backed framebuffer.1311 * @user_fence_rep: User-space provided structure for fence information.1312 * Must be set to non-NULL if @file_priv is non-NULL.1313 * @vclips: Array of clip rects.1314 * @num_clips: Number of clip rects in @vclips.1315 * @crtc: If crtc is passed, readback on that crtc only.1316 *1317 * Returns 0 on success, negative error code on failure. -ERESTARTSYS if1318 * interrupted.1319 */1320int vmw_kms_sou_readback(struct vmw_private *dev_priv,1321 struct drm_file *file_priv,1322 struct vmw_framebuffer *vfb,1323 struct drm_vmw_fence_rep __user *user_fence_rep,1324 struct drm_vmw_rect *vclips,1325 uint32_t num_clips,1326 struct drm_crtc *crtc)1327{1328 struct vmw_bo *buf =1329 container_of(vfb, struct vmw_framebuffer_bo, base)->buffer;1330 struct vmw_kms_dirty dirty;1331 DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);1332 int ret;1333 1334 vmw_bo_placement_set(buf, VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM,1335 VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM);1336 ret = vmw_validation_add_bo(&val_ctx, buf);1337 if (ret)1338 return ret;1339 1340 ret = vmw_validation_prepare(&val_ctx, NULL, true);1341 if (ret)1342 goto out_unref;1343 1344 ret = do_bo_define_gmrfb(dev_priv, vfb);1345 if (unlikely(ret != 0))1346 goto out_revert;1347 1348 dirty.crtc = crtc;1349 dirty.fifo_commit = vmw_sou_readback_fifo_commit;1350 dirty.clip = vmw_sou_readback_clip;1351 dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_readback_blit) *1352 num_clips;1353 ret = vmw_kms_helper_dirty(dev_priv, vfb, NULL, vclips,1354 0, 0, num_clips, 1, &dirty);1355 vmw_kms_helper_validation_finish(dev_priv, file_priv, &val_ctx, NULL,1356 user_fence_rep);1357 1358 return ret;1359 1360out_revert:1361 vmw_validation_revert(&val_ctx);1362out_unref:1363 vmw_validation_unref_lists(&val_ctx);1364 1365 return ret;1366}1367