brintos

brintos / linux-shallow public Read only

0
0
Text · 38.1 KiB · 0f4bfd9 Raw
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