brintos

brintos / linux-shallow public Read only

0
0
Text · 49.4 KiB · 82d18b8 Raw
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