162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/**************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the
862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
1262306a36Sopenharmony_ci * the following conditions:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
1662306a36Sopenharmony_ci * of the Software.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci **************************************************************************/
2762306a36Sopenharmony_ci#include "vmwgfx_kms.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "vmwgfx_bo.h"
3062306a36Sopenharmony_ci#include "vmw_surface_cache.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <drm/drm_atomic.h>
3362306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
3462306a36Sopenharmony_ci#include <drm/drm_damage_helper.h>
3562306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3662306a36Sopenharmony_ci#include <drm/drm_rect.h>
3762306a36Sopenharmony_ci#include <drm/drm_sysfs.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_civoid vmw_du_cleanup(struct vmw_display_unit *du)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(du->primary.dev);
4262306a36Sopenharmony_ci	drm_plane_cleanup(&du->primary);
4362306a36Sopenharmony_ci	if (vmw_cmd_supported(dev_priv))
4462306a36Sopenharmony_ci		drm_plane_cleanup(&du->cursor.base);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	drm_connector_unregister(&du->connector);
4762306a36Sopenharmony_ci	drm_crtc_cleanup(&du->crtc);
4862306a36Sopenharmony_ci	drm_encoder_cleanup(&du->encoder);
4962306a36Sopenharmony_ci	drm_connector_cleanup(&du->connector);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * Display Unit Cursor functions
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps);
5762306a36Sopenharmony_cistatic void vmw_cursor_update_mob(struct vmw_private *dev_priv,
5862306a36Sopenharmony_ci				  struct vmw_plane_state *vps,
5962306a36Sopenharmony_ci				  u32 *image, u32 width, u32 height,
6062306a36Sopenharmony_ci				  u32 hotspotX, u32 hotspotY);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct vmw_svga_fifo_cmd_define_cursor {
6362306a36Sopenharmony_ci	u32 cmd;
6462306a36Sopenharmony_ci	SVGAFifoCmdDefineAlphaCursor cursor;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * vmw_send_define_cursor_cmd - queue a define cursor command
6962306a36Sopenharmony_ci * @dev_priv: the private driver struct
7062306a36Sopenharmony_ci * @image: buffer which holds the cursor image
7162306a36Sopenharmony_ci * @width: width of the mouse cursor image
7262306a36Sopenharmony_ci * @height: height of the mouse cursor image
7362306a36Sopenharmony_ci * @hotspotX: the horizontal position of mouse hotspot
7462306a36Sopenharmony_ci * @hotspotY: the vertical position of mouse hotspot
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic void vmw_send_define_cursor_cmd(struct vmw_private *dev_priv,
7762306a36Sopenharmony_ci				       u32 *image, u32 width, u32 height,
7862306a36Sopenharmony_ci				       u32 hotspotX, u32 hotspotY)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct vmw_svga_fifo_cmd_define_cursor *cmd;
8162306a36Sopenharmony_ci	const u32 image_size = width * height * sizeof(*image);
8262306a36Sopenharmony_ci	const u32 cmd_size = sizeof(*cmd) + image_size;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Try to reserve fifocmd space and swallow any failures;
8562306a36Sopenharmony_ci	   such reservations cannot be left unconsumed for long
8662306a36Sopenharmony_ci	   under the risk of clogging other fifocmd users, so
8762306a36Sopenharmony_ci	   we treat reservations separtely from the way we treat
8862306a36Sopenharmony_ci	   other fallible KMS-atomic resources at prepare_fb */
8962306a36Sopenharmony_ci	cmd = VMW_CMD_RESERVE(dev_priv, cmd_size);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (unlikely(!cmd))
9262306a36Sopenharmony_ci		return;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	memset(cmd, 0, sizeof(*cmd));
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	memcpy(&cmd[1], image, image_size);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR;
9962306a36Sopenharmony_ci	cmd->cursor.id = 0;
10062306a36Sopenharmony_ci	cmd->cursor.width = width;
10162306a36Sopenharmony_ci	cmd->cursor.height = height;
10262306a36Sopenharmony_ci	cmd->cursor.hotspotX = hotspotX;
10362306a36Sopenharmony_ci	cmd->cursor.hotspotY = hotspotY;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	vmw_cmd_commit_flush(dev_priv, cmd_size);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/**
10962306a36Sopenharmony_ci * vmw_cursor_update_image - update the cursor image on the provided plane
11062306a36Sopenharmony_ci * @dev_priv: the private driver struct
11162306a36Sopenharmony_ci * @vps: the plane state of the cursor plane
11262306a36Sopenharmony_ci * @image: buffer which holds the cursor image
11362306a36Sopenharmony_ci * @width: width of the mouse cursor image
11462306a36Sopenharmony_ci * @height: height of the mouse cursor image
11562306a36Sopenharmony_ci * @hotspotX: the horizontal position of mouse hotspot
11662306a36Sopenharmony_ci * @hotspotY: the vertical position of mouse hotspot
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_cistatic void vmw_cursor_update_image(struct vmw_private *dev_priv,
11962306a36Sopenharmony_ci				    struct vmw_plane_state *vps,
12062306a36Sopenharmony_ci				    u32 *image, u32 width, u32 height,
12162306a36Sopenharmony_ci				    u32 hotspotX, u32 hotspotY)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	if (vps->cursor.bo)
12462306a36Sopenharmony_ci		vmw_cursor_update_mob(dev_priv, vps, image,
12562306a36Sopenharmony_ci				      vps->base.crtc_w, vps->base.crtc_h,
12662306a36Sopenharmony_ci				      hotspotX, hotspotY);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	else
12962306a36Sopenharmony_ci		vmw_send_define_cursor_cmd(dev_priv, image, width, height,
13062306a36Sopenharmony_ci					   hotspotX, hotspotY);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * Called from inside vmw_du_cursor_plane_atomic_update to actually
13862306a36Sopenharmony_ci * make the cursor-image live.
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * @dev_priv: device to work with
14162306a36Sopenharmony_ci * @vps: the plane state of the cursor plane
14262306a36Sopenharmony_ci * @image: cursor source data to fill the MOB with
14362306a36Sopenharmony_ci * @width: source data width
14462306a36Sopenharmony_ci * @height: source data height
14562306a36Sopenharmony_ci * @hotspotX: cursor hotspot x
14662306a36Sopenharmony_ci * @hotspotY: cursor hotspot Y
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic void vmw_cursor_update_mob(struct vmw_private *dev_priv,
14962306a36Sopenharmony_ci				  struct vmw_plane_state *vps,
15062306a36Sopenharmony_ci				  u32 *image, u32 width, u32 height,
15162306a36Sopenharmony_ci				  u32 hotspotX, u32 hotspotY)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	SVGAGBCursorHeader *header;
15462306a36Sopenharmony_ci	SVGAGBAlphaCursorHeader *alpha_header;
15562306a36Sopenharmony_ci	const u32 image_size = width * height * sizeof(*image);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	header = vmw_bo_map_and_cache(vps->cursor.bo);
15862306a36Sopenharmony_ci	alpha_header = &header->header.alphaHeader;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	memset(header, 0, sizeof(*header));
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	header->type = SVGA_ALPHA_CURSOR;
16362306a36Sopenharmony_ci	header->sizeInBytes = image_size;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	alpha_header->hotspotX = hotspotX;
16662306a36Sopenharmony_ci	alpha_header->hotspotY = hotspotY;
16762306a36Sopenharmony_ci	alpha_header->width = width;
16862306a36Sopenharmony_ci	alpha_header->height = height;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	memcpy(header + 1, image, image_size);
17162306a36Sopenharmony_ci	vmw_write(dev_priv, SVGA_REG_CURSOR_MOBID,
17262306a36Sopenharmony_ci		  vps->cursor.bo->tbo.resource->start);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic u32 vmw_du_cursor_mob_size(u32 w, u32 h)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * vmw_du_cursor_plane_acquire_image -- Acquire the image data
18362306a36Sopenharmony_ci * @vps: cursor plane state
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_cistatic u32 *vmw_du_cursor_plane_acquire_image(struct vmw_plane_state *vps)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	if (vps->surf) {
18862306a36Sopenharmony_ci		if (vps->surf_mapped)
18962306a36Sopenharmony_ci			return vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo);
19062306a36Sopenharmony_ci		return vps->surf->snooper.image;
19162306a36Sopenharmony_ci	} else if (vps->bo)
19262306a36Sopenharmony_ci		return vmw_bo_map_and_cache(vps->bo);
19362306a36Sopenharmony_ci	return NULL;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps,
19762306a36Sopenharmony_ci					    struct vmw_plane_state *new_vps)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	void *old_image;
20062306a36Sopenharmony_ci	void *new_image;
20162306a36Sopenharmony_ci	u32 size;
20262306a36Sopenharmony_ci	bool changed;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (old_vps->base.crtc_w != new_vps->base.crtc_w ||
20562306a36Sopenharmony_ci	    old_vps->base.crtc_h != new_vps->base.crtc_h)
20662306a36Sopenharmony_ci	    return true;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (old_vps->cursor.hotspot_x != new_vps->cursor.hotspot_x ||
20962306a36Sopenharmony_ci	    old_vps->cursor.hotspot_y != new_vps->cursor.hotspot_y)
21062306a36Sopenharmony_ci	    return true;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	size = new_vps->base.crtc_w * new_vps->base.crtc_h * sizeof(u32);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	old_image = vmw_du_cursor_plane_acquire_image(old_vps);
21562306a36Sopenharmony_ci	new_image = vmw_du_cursor_plane_acquire_image(new_vps);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	changed = false;
21862306a36Sopenharmony_ci	if (old_image && new_image)
21962306a36Sopenharmony_ci		changed = memcmp(old_image, new_image, size) != 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return changed;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void vmw_du_destroy_cursor_mob(struct vmw_bo **vbo)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	if (!(*vbo))
22762306a36Sopenharmony_ci		return;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ttm_bo_unpin(&(*vbo)->tbo);
23062306a36Sopenharmony_ci	vmw_bo_unreference(vbo);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void vmw_du_put_cursor_mob(struct vmw_cursor_plane *vcp,
23462306a36Sopenharmony_ci				  struct vmw_plane_state *vps)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	u32 i;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (!vps->cursor.bo)
23962306a36Sopenharmony_ci		return;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	vmw_du_cursor_plane_unmap_cm(vps);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* Look for a free slot to return this mob to the cache. */
24462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) {
24562306a36Sopenharmony_ci		if (!vcp->cursor_mobs[i]) {
24662306a36Sopenharmony_ci			vcp->cursor_mobs[i] = vps->cursor.bo;
24762306a36Sopenharmony_ci			vps->cursor.bo = NULL;
24862306a36Sopenharmony_ci			return;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Cache is full: See if this mob is bigger than an existing mob. */
25362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) {
25462306a36Sopenharmony_ci		if (vcp->cursor_mobs[i]->tbo.base.size <
25562306a36Sopenharmony_ci		    vps->cursor.bo->tbo.base.size) {
25662306a36Sopenharmony_ci			vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]);
25762306a36Sopenharmony_ci			vcp->cursor_mobs[i] = vps->cursor.bo;
25862306a36Sopenharmony_ci			vps->cursor.bo = NULL;
25962306a36Sopenharmony_ci			return;
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* Destroy it if it's not worth caching. */
26462306a36Sopenharmony_ci	vmw_du_destroy_cursor_mob(&vps->cursor.bo);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int vmw_du_get_cursor_mob(struct vmw_cursor_plane *vcp,
26862306a36Sopenharmony_ci				 struct vmw_plane_state *vps)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct vmw_private *dev_priv = vcp->base.dev->dev_private;
27162306a36Sopenharmony_ci	u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h);
27262306a36Sopenharmony_ci	u32 i;
27362306a36Sopenharmony_ci	u32 cursor_max_dim, mob_max_size;
27462306a36Sopenharmony_ci	struct vmw_fence_obj *fence = NULL;
27562306a36Sopenharmony_ci	int ret;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (!dev_priv->has_mob ||
27862306a36Sopenharmony_ci	    (dev_priv->capabilities2 & SVGA_CAP2_CURSOR_MOB) == 0)
27962306a36Sopenharmony_ci		return -EINVAL;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	mob_max_size = vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE);
28262306a36Sopenharmony_ci	cursor_max_dim = vmw_read(dev_priv, SVGA_REG_CURSOR_MAX_DIMENSION);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (size > mob_max_size || vps->base.crtc_w > cursor_max_dim ||
28562306a36Sopenharmony_ci	    vps->base.crtc_h > cursor_max_dim)
28662306a36Sopenharmony_ci		return -EINVAL;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (vps->cursor.bo) {
28962306a36Sopenharmony_ci		if (vps->cursor.bo->tbo.base.size >= size)
29062306a36Sopenharmony_ci			return 0;
29162306a36Sopenharmony_ci		vmw_du_put_cursor_mob(vcp, vps);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* Look for an unused mob in the cache. */
29562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) {
29662306a36Sopenharmony_ci		if (vcp->cursor_mobs[i] &&
29762306a36Sopenharmony_ci		    vcp->cursor_mobs[i]->tbo.base.size >= size) {
29862306a36Sopenharmony_ci			vps->cursor.bo = vcp->cursor_mobs[i];
29962306a36Sopenharmony_ci			vcp->cursor_mobs[i] = NULL;
30062306a36Sopenharmony_ci			return 0;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci	/* Create a new mob if we can't find an existing one. */
30462306a36Sopenharmony_ci	ret = vmw_bo_create_and_populate(dev_priv, size,
30562306a36Sopenharmony_ci					 VMW_BO_DOMAIN_MOB,
30662306a36Sopenharmony_ci					 &vps->cursor.bo);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (ret != 0)
30962306a36Sopenharmony_ci		return ret;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Fence the mob creation so we are guarateed to have the mob */
31262306a36Sopenharmony_ci	ret = ttm_bo_reserve(&vps->cursor.bo->tbo, false, false, NULL);
31362306a36Sopenharmony_ci	if (ret != 0)
31462306a36Sopenharmony_ci		goto teardown;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ret = vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
31762306a36Sopenharmony_ci	if (ret != 0) {
31862306a36Sopenharmony_ci		ttm_bo_unreserve(&vps->cursor.bo->tbo);
31962306a36Sopenharmony_ci		goto teardown;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dma_fence_wait(&fence->base, false);
32362306a36Sopenharmony_ci	dma_fence_put(&fence->base);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ttm_bo_unreserve(&vps->cursor.bo->tbo);
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_citeardown:
32962306a36Sopenharmony_ci	vmw_du_destroy_cursor_mob(&vps->cursor.bo);
33062306a36Sopenharmony_ci	return ret;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void vmw_cursor_update_position(struct vmw_private *dev_priv,
33562306a36Sopenharmony_ci				       bool show, int x, int y)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	const uint32_t svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW
33862306a36Sopenharmony_ci					     : SVGA_CURSOR_ON_HIDE;
33962306a36Sopenharmony_ci	uint32_t count;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	spin_lock(&dev_priv->cursor_lock);
34262306a36Sopenharmony_ci	if (dev_priv->capabilities2 & SVGA_CAP2_EXTRA_REGS) {
34362306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR4_X, x);
34462306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR4_Y, y);
34562306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR4_SCREEN_ID, SVGA3D_INVALID_ID);
34662306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR4_ON, svga_cursor_on);
34762306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR4_SUBMIT, 1);
34862306a36Sopenharmony_ci	} else if (vmw_is_cursor_bypass3_enabled(dev_priv)) {
34962306a36Sopenharmony_ci		vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, svga_cursor_on);
35062306a36Sopenharmony_ci		vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_X, x);
35162306a36Sopenharmony_ci		vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_Y, y);
35262306a36Sopenharmony_ci		count = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CURSOR_COUNT);
35362306a36Sopenharmony_ci		vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_COUNT, ++count);
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR_X, x);
35662306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR_Y, y);
35762306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_CURSOR_ON, svga_cursor_on);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci	spin_unlock(&dev_priv->cursor_lock);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_civoid vmw_kms_cursor_snoop(struct vmw_surface *srf,
36362306a36Sopenharmony_ci			  struct ttm_object_file *tfile,
36462306a36Sopenharmony_ci			  struct ttm_buffer_object *bo,
36562306a36Sopenharmony_ci			  SVGA3dCmdHeader *header)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct ttm_bo_kmap_obj map;
36862306a36Sopenharmony_ci	unsigned long kmap_offset;
36962306a36Sopenharmony_ci	unsigned long kmap_num;
37062306a36Sopenharmony_ci	SVGA3dCopyBox *box;
37162306a36Sopenharmony_ci	unsigned box_count;
37262306a36Sopenharmony_ci	void *virtual;
37362306a36Sopenharmony_ci	bool is_iomem;
37462306a36Sopenharmony_ci	struct vmw_dma_cmd {
37562306a36Sopenharmony_ci		SVGA3dCmdHeader header;
37662306a36Sopenharmony_ci		SVGA3dCmdSurfaceDMA dma;
37762306a36Sopenharmony_ci	} *cmd;
37862306a36Sopenharmony_ci	int i, ret;
37962306a36Sopenharmony_ci	const struct SVGA3dSurfaceDesc *desc =
38062306a36Sopenharmony_ci		vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT);
38162306a36Sopenharmony_ci	const u32 image_pitch = VMW_CURSOR_SNOOP_WIDTH * desc->pitchBytesPerBlock;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	cmd = container_of(header, struct vmw_dma_cmd, header);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* No snooper installed, nothing to copy */
38662306a36Sopenharmony_ci	if (!srf->snooper.image)
38762306a36Sopenharmony_ci		return;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
39062306a36Sopenharmony_ci		DRM_ERROR("face and mipmap for cursors should never != 0\n");
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (cmd->header.size < 64) {
39562306a36Sopenharmony_ci		DRM_ERROR("at least one full copy box must be given\n");
39662306a36Sopenharmony_ci		return;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	box = (SVGA3dCopyBox *)&cmd[1];
40062306a36Sopenharmony_ci	box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
40162306a36Sopenharmony_ci			sizeof(SVGA3dCopyBox);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (cmd->dma.guest.ptr.offset % PAGE_SIZE ||
40462306a36Sopenharmony_ci	    box->x != 0    || box->y != 0    || box->z != 0    ||
40562306a36Sopenharmony_ci	    box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
40662306a36Sopenharmony_ci	    box->d != 1    || box_count != 1 ||
40762306a36Sopenharmony_ci	    box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT) {
40862306a36Sopenharmony_ci		/* TODO handle none page aligned offsets */
40962306a36Sopenharmony_ci		/* TODO handle more dst & src != 0 */
41062306a36Sopenharmony_ci		/* TODO handle more then one copy */
41162306a36Sopenharmony_ci		DRM_ERROR("Can't snoop dma request for cursor!\n");
41262306a36Sopenharmony_ci		DRM_ERROR("(%u, %u, %u) (%u, %u, %u) (%ux%ux%u) %u %u\n",
41362306a36Sopenharmony_ci			  box->srcx, box->srcy, box->srcz,
41462306a36Sopenharmony_ci			  box->x, box->y, box->z,
41562306a36Sopenharmony_ci			  box->w, box->h, box->d, box_count,
41662306a36Sopenharmony_ci			  cmd->dma.guest.ptr.offset);
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
42162306a36Sopenharmony_ci	kmap_num = (VMW_CURSOR_SNOOP_HEIGHT*image_pitch) >> PAGE_SHIFT;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ret = ttm_bo_reserve(bo, true, false, NULL);
42462306a36Sopenharmony_ci	if (unlikely(ret != 0)) {
42562306a36Sopenharmony_ci		DRM_ERROR("reserve failed\n");
42662306a36Sopenharmony_ci		return;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
43062306a36Sopenharmony_ci	if (unlikely(ret != 0))
43162306a36Sopenharmony_ci		goto err_unreserve;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	virtual = ttm_kmap_obj_virtual(&map, &is_iomem);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (box->w == VMW_CURSOR_SNOOP_WIDTH && cmd->dma.guest.pitch == image_pitch) {
43662306a36Sopenharmony_ci		memcpy(srf->snooper.image, virtual,
43762306a36Sopenharmony_ci		       VMW_CURSOR_SNOOP_HEIGHT*image_pitch);
43862306a36Sopenharmony_ci	} else {
43962306a36Sopenharmony_ci		/* Image is unsigned pointer. */
44062306a36Sopenharmony_ci		for (i = 0; i < box->h; i++)
44162306a36Sopenharmony_ci			memcpy(srf->snooper.image + i * image_pitch,
44262306a36Sopenharmony_ci			       virtual + i * cmd->dma.guest.pitch,
44362306a36Sopenharmony_ci			       box->w * desc->pitchBytesPerBlock);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	srf->snooper.age++;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ttm_bo_kunmap(&map);
44962306a36Sopenharmony_cierr_unreserve:
45062306a36Sopenharmony_ci	ttm_bo_unreserve(bo);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * @dev_priv: Pointer to the device private struct.
45762306a36Sopenharmony_ci *
45862306a36Sopenharmony_ci * Clears all legacy hotspots.
45962306a36Sopenharmony_ci */
46062306a36Sopenharmony_civoid vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
46362306a36Sopenharmony_ci	struct vmw_display_unit *du;
46462306a36Sopenharmony_ci	struct drm_crtc *crtc;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	drm_modeset_lock_all(dev);
46762306a36Sopenharmony_ci	drm_for_each_crtc(crtc, dev) {
46862306a36Sopenharmony_ci		du = vmw_crtc_to_du(crtc);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		du->hotspot_x = 0;
47162306a36Sopenharmony_ci		du->hotspot_y = 0;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	drm_modeset_unlock_all(dev);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_civoid vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
47962306a36Sopenharmony_ci	struct vmw_display_unit *du;
48062306a36Sopenharmony_ci	struct drm_crtc *crtc;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
48562306a36Sopenharmony_ci		du = vmw_crtc_to_du(crtc);
48662306a36Sopenharmony_ci		if (!du->cursor_surface ||
48762306a36Sopenharmony_ci		    du->cursor_age == du->cursor_surface->snooper.age ||
48862306a36Sopenharmony_ci		    !du->cursor_surface->snooper.image)
48962306a36Sopenharmony_ci			continue;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		du->cursor_age = du->cursor_surface->snooper.age;
49262306a36Sopenharmony_ci		vmw_send_define_cursor_cmd(dev_priv,
49362306a36Sopenharmony_ci					   du->cursor_surface->snooper.image,
49462306a36Sopenharmony_ci					   VMW_CURSOR_SNOOP_WIDTH,
49562306a36Sopenharmony_ci					   VMW_CURSOR_SNOOP_HEIGHT,
49662306a36Sopenharmony_ci					   du->hotspot_x + du->core_hotspot_x,
49762306a36Sopenharmony_ci					   du->hotspot_y + du->core_hotspot_y);
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_civoid vmw_du_cursor_plane_destroy(struct drm_plane *plane)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane);
50762306a36Sopenharmony_ci	u32 i;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++)
51262306a36Sopenharmony_ci		vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	drm_plane_cleanup(plane);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_civoid vmw_du_primary_plane_destroy(struct drm_plane *plane)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	drm_plane_cleanup(plane);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* Planes are static in our case so we don't free it */
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/**
52762306a36Sopenharmony_ci * vmw_du_plane_unpin_surf - unpins resource associated with a framebuffer surface
52862306a36Sopenharmony_ci *
52962306a36Sopenharmony_ci * @vps: plane state associated with the display surface
53062306a36Sopenharmony_ci * @unreference: true if we also want to unreference the display.
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_civoid vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
53362306a36Sopenharmony_ci			     bool unreference)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	if (vps->surf) {
53662306a36Sopenharmony_ci		if (vps->pinned) {
53762306a36Sopenharmony_ci			vmw_resource_unpin(&vps->surf->res);
53862306a36Sopenharmony_ci			vps->pinned--;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		if (unreference) {
54262306a36Sopenharmony_ci			if (vps->pinned)
54362306a36Sopenharmony_ci				DRM_ERROR("Surface still pinned\n");
54462306a36Sopenharmony_ci			vmw_surface_unreference(&vps->surf);
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/**
55162306a36Sopenharmony_ci * vmw_du_plane_cleanup_fb - Unpins the plane surface
55262306a36Sopenharmony_ci *
55362306a36Sopenharmony_ci * @plane:  display plane
55462306a36Sopenharmony_ci * @old_state: Contains the FB to clean up
55562306a36Sopenharmony_ci *
55662306a36Sopenharmony_ci * Unpins the framebuffer surface
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * Returns 0 on success
55962306a36Sopenharmony_ci */
56062306a36Sopenharmony_civoid
56162306a36Sopenharmony_civmw_du_plane_cleanup_fb(struct drm_plane *plane,
56262306a36Sopenharmony_ci			struct drm_plane_state *old_state)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	vmw_du_plane_unpin_surf(vps, false);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci/**
57162306a36Sopenharmony_ci * vmw_du_cursor_plane_map_cm - Maps the cursor mobs.
57262306a36Sopenharmony_ci *
57362306a36Sopenharmony_ci * @vps: plane_state
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * Returns 0 on success
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int
57962306a36Sopenharmony_civmw_du_cursor_plane_map_cm(struct vmw_plane_state *vps)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	int ret;
58262306a36Sopenharmony_ci	u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h);
58362306a36Sopenharmony_ci	struct ttm_buffer_object *bo;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (!vps->cursor.bo)
58662306a36Sopenharmony_ci		return -EINVAL;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	bo = &vps->cursor.bo->tbo;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (bo->base.size < size)
59162306a36Sopenharmony_ci		return -EINVAL;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (vps->cursor.bo->map.virtual)
59462306a36Sopenharmony_ci		return 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	ret = ttm_bo_reserve(bo, false, false, NULL);
59762306a36Sopenharmony_ci	if (unlikely(ret != 0))
59862306a36Sopenharmony_ci		return -ENOMEM;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	vmw_bo_map_and_cache(vps->cursor.bo);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	ttm_bo_unreserve(bo);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (unlikely(ret != 0))
60562306a36Sopenharmony_ci		return -ENOMEM;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/**
61262306a36Sopenharmony_ci * vmw_du_cursor_plane_unmap_cm - Unmaps the cursor mobs.
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * @vps: state of the cursor plane
61562306a36Sopenharmony_ci *
61662306a36Sopenharmony_ci * Returns 0 on success
61762306a36Sopenharmony_ci */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int
62062306a36Sopenharmony_civmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int ret = 0;
62362306a36Sopenharmony_ci	struct vmw_bo *vbo = vps->cursor.bo;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (!vbo || !vbo->map.virtual)
62662306a36Sopenharmony_ci		return 0;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	ret = ttm_bo_reserve(&vbo->tbo, true, false, NULL);
62962306a36Sopenharmony_ci	if (likely(ret == 0)) {
63062306a36Sopenharmony_ci		vmw_bo_unmap(vbo);
63162306a36Sopenharmony_ci		ttm_bo_unreserve(&vbo->tbo);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return ret;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * @plane: cursor plane
64262306a36Sopenharmony_ci * @old_state: contains the state to clean up
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Unmaps all cursor bo mappings and unpins the cursor surface
64562306a36Sopenharmony_ci *
64662306a36Sopenharmony_ci * Returns 0 on success
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_civoid
64962306a36Sopenharmony_civmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane,
65062306a36Sopenharmony_ci			       struct drm_plane_state *old_state)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane);
65362306a36Sopenharmony_ci	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (vps->surf_mapped) {
65662306a36Sopenharmony_ci		vmw_bo_unmap(vps->surf->res.guest_memory_bo);
65762306a36Sopenharmony_ci		vps->surf_mapped = false;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	vmw_du_cursor_plane_unmap_cm(vps);
66162306a36Sopenharmony_ci	vmw_du_put_cursor_mob(vcp, vps);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	vmw_du_plane_unpin_surf(vps, false);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (vps->surf) {
66662306a36Sopenharmony_ci		vmw_surface_unreference(&vps->surf);
66762306a36Sopenharmony_ci		vps->surf = NULL;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (vps->bo) {
67162306a36Sopenharmony_ci		vmw_bo_unreference(&vps->bo);
67262306a36Sopenharmony_ci		vps->bo = NULL;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci/**
67862306a36Sopenharmony_ci * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it
67962306a36Sopenharmony_ci *
68062306a36Sopenharmony_ci * @plane:  display plane
68162306a36Sopenharmony_ci * @new_state: info on the new plane state, including the FB
68262306a36Sopenharmony_ci *
68362306a36Sopenharmony_ci * Returns 0 on success
68462306a36Sopenharmony_ci */
68562306a36Sopenharmony_ciint
68662306a36Sopenharmony_civmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
68762306a36Sopenharmony_ci			       struct drm_plane_state *new_state)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
69062306a36Sopenharmony_ci	struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane);
69162306a36Sopenharmony_ci	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
69262306a36Sopenharmony_ci	int ret = 0;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (vps->surf) {
69562306a36Sopenharmony_ci		if (vps->surf_mapped) {
69662306a36Sopenharmony_ci			vmw_bo_unmap(vps->surf->res.guest_memory_bo);
69762306a36Sopenharmony_ci			vps->surf_mapped = false;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci		vmw_surface_unreference(&vps->surf);
70062306a36Sopenharmony_ci		vps->surf = NULL;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (vps->bo) {
70462306a36Sopenharmony_ci		vmw_bo_unreference(&vps->bo);
70562306a36Sopenharmony_ci		vps->bo = NULL;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (fb) {
70962306a36Sopenharmony_ci		if (vmw_framebuffer_to_vfb(fb)->bo) {
71062306a36Sopenharmony_ci			vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer;
71162306a36Sopenharmony_ci			vmw_bo_reference(vps->bo);
71262306a36Sopenharmony_ci		} else {
71362306a36Sopenharmony_ci			vps->surf = vmw_framebuffer_to_vfbs(fb)->surface;
71462306a36Sopenharmony_ci			vmw_surface_reference(vps->surf);
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (!vps->surf && vps->bo) {
71962306a36Sopenharmony_ci		const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		/*
72262306a36Sopenharmony_ci		 * Not using vmw_bo_map_and_cache() helper here as we need to
72362306a36Sopenharmony_ci		 * reserve the ttm_buffer_object first which
72462306a36Sopenharmony_ci		 * vmw_bo_map_and_cache() omits.
72562306a36Sopenharmony_ci		 */
72662306a36Sopenharmony_ci		ret = ttm_bo_reserve(&vps->bo->tbo, true, false, NULL);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		if (unlikely(ret != 0))
72962306a36Sopenharmony_ci			return -ENOMEM;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		ret = ttm_bo_kmap(&vps->bo->tbo, 0, PFN_UP(size), &vps->bo->map);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		ttm_bo_unreserve(&vps->bo->tbo);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (unlikely(ret != 0))
73662306a36Sopenharmony_ci			return -ENOMEM;
73762306a36Sopenharmony_ci	} else if (vps->surf && !vps->bo && vps->surf->res.guest_memory_bo) {
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		WARN_ON(vps->surf->snooper.image);
74062306a36Sopenharmony_ci		ret = ttm_bo_reserve(&vps->surf->res.guest_memory_bo->tbo, true, false,
74162306a36Sopenharmony_ci				     NULL);
74262306a36Sopenharmony_ci		if (unlikely(ret != 0))
74362306a36Sopenharmony_ci			return -ENOMEM;
74462306a36Sopenharmony_ci		vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo);
74562306a36Sopenharmony_ci		ttm_bo_unreserve(&vps->surf->res.guest_memory_bo->tbo);
74662306a36Sopenharmony_ci		vps->surf_mapped = true;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (vps->surf || vps->bo) {
75062306a36Sopenharmony_ci		vmw_du_get_cursor_mob(vcp, vps);
75162306a36Sopenharmony_ci		vmw_du_cursor_plane_map_cm(vps);
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_civoid
75962306a36Sopenharmony_civmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
76062306a36Sopenharmony_ci				  struct drm_atomic_state *state)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
76362306a36Sopenharmony_ci									   plane);
76462306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
76562306a36Sopenharmony_ci									   plane);
76662306a36Sopenharmony_ci	struct drm_crtc *crtc = new_state->crtc ?: old_state->crtc;
76762306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
76862306a36Sopenharmony_ci	struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
76962306a36Sopenharmony_ci	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
77062306a36Sopenharmony_ci	struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(old_state);
77162306a36Sopenharmony_ci	s32 hotspot_x, hotspot_y;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	hotspot_x = du->hotspot_x;
77462306a36Sopenharmony_ci	hotspot_y = du->hotspot_y;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (new_state->fb) {
77762306a36Sopenharmony_ci		hotspot_x += new_state->fb->hot_x;
77862306a36Sopenharmony_ci		hotspot_y += new_state->fb->hot_y;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	du->cursor_surface = vps->surf;
78262306a36Sopenharmony_ci	du->cursor_bo = vps->bo;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!vps->surf && !vps->bo) {
78562306a36Sopenharmony_ci		vmw_cursor_update_position(dev_priv, false, 0, 0);
78662306a36Sopenharmony_ci		return;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	vps->cursor.hotspot_x = hotspot_x;
79062306a36Sopenharmony_ci	vps->cursor.hotspot_y = hotspot_y;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (vps->surf) {
79362306a36Sopenharmony_ci		du->cursor_age = du->cursor_surface->snooper.age;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (!vmw_du_cursor_plane_has_changed(old_vps, vps)) {
79762306a36Sopenharmony_ci		/*
79862306a36Sopenharmony_ci		 * If it hasn't changed, avoid making the device do extra
79962306a36Sopenharmony_ci		 * work by keeping the old cursor active.
80062306a36Sopenharmony_ci		 */
80162306a36Sopenharmony_ci		struct vmw_cursor_plane_state tmp = old_vps->cursor;
80262306a36Sopenharmony_ci		old_vps->cursor = vps->cursor;
80362306a36Sopenharmony_ci		vps->cursor = tmp;
80462306a36Sopenharmony_ci	} else {
80562306a36Sopenharmony_ci		void *image = vmw_du_cursor_plane_acquire_image(vps);
80662306a36Sopenharmony_ci		if (image)
80762306a36Sopenharmony_ci			vmw_cursor_update_image(dev_priv, vps, image,
80862306a36Sopenharmony_ci						new_state->crtc_w,
80962306a36Sopenharmony_ci						new_state->crtc_h,
81062306a36Sopenharmony_ci						hotspot_x, hotspot_y);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	du->cursor_x = new_state->crtc_x + du->set_gui_x;
81462306a36Sopenharmony_ci	du->cursor_y = new_state->crtc_y + du->set_gui_y;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	vmw_cursor_update_position(dev_priv, true,
81762306a36Sopenharmony_ci				   du->cursor_x + hotspot_x,
81862306a36Sopenharmony_ci				   du->cursor_y + hotspot_y);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	du->core_hotspot_x = hotspot_x - du->hotspot_x;
82162306a36Sopenharmony_ci	du->core_hotspot_y = hotspot_y - du->hotspot_y;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/**
82662306a36Sopenharmony_ci * vmw_du_primary_plane_atomic_check - check if the new state is okay
82762306a36Sopenharmony_ci *
82862306a36Sopenharmony_ci * @plane: display plane
82962306a36Sopenharmony_ci * @state: info on the new plane state, including the FB
83062306a36Sopenharmony_ci *
83162306a36Sopenharmony_ci * Check if the new state is settable given the current state.  Other
83262306a36Sopenharmony_ci * than what the atomic helper checks, we care about crtc fitting
83362306a36Sopenharmony_ci * the FB and maintaining one active framebuffer.
83462306a36Sopenharmony_ci *
83562306a36Sopenharmony_ci * Returns 0 on success
83662306a36Sopenharmony_ci */
83762306a36Sopenharmony_ciint vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
83862306a36Sopenharmony_ci				      struct drm_atomic_state *state)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
84162306a36Sopenharmony_ci									   plane);
84262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = NULL;
84362306a36Sopenharmony_ci	struct drm_framebuffer *new_fb = new_state->fb;
84462306a36Sopenharmony_ci	int ret;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (new_state->crtc)
84762306a36Sopenharmony_ci		crtc_state = drm_atomic_get_new_crtc_state(state,
84862306a36Sopenharmony_ci							   new_state->crtc);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_state, crtc_state,
85162306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
85262306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
85362306a36Sopenharmony_ci						  false, true);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (!ret && new_fb) {
85662306a36Sopenharmony_ci		struct drm_crtc *crtc = new_state->crtc;
85762306a36Sopenharmony_ci		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		vmw_connector_state_to_vcs(du->connector.state);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	return ret;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/**
86862306a36Sopenharmony_ci * vmw_du_cursor_plane_atomic_check - check if the new state is okay
86962306a36Sopenharmony_ci *
87062306a36Sopenharmony_ci * @plane: cursor plane
87162306a36Sopenharmony_ci * @state: info on the new plane state
87262306a36Sopenharmony_ci *
87362306a36Sopenharmony_ci * This is a chance to fail if the new cursor state does not fit
87462306a36Sopenharmony_ci * our requirements.
87562306a36Sopenharmony_ci *
87662306a36Sopenharmony_ci * Returns 0 on success
87762306a36Sopenharmony_ci */
87862306a36Sopenharmony_ciint vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
87962306a36Sopenharmony_ci				     struct drm_atomic_state *state)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
88262306a36Sopenharmony_ci									   plane);
88362306a36Sopenharmony_ci	int ret = 0;
88462306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = NULL;
88562306a36Sopenharmony_ci	struct vmw_surface *surface = NULL;
88662306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (new_state->crtc)
88962306a36Sopenharmony_ci		crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
89062306a36Sopenharmony_ci							   new_state->crtc);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_state, crtc_state,
89362306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
89462306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
89562306a36Sopenharmony_ci						  true, true);
89662306a36Sopenharmony_ci	if (ret)
89762306a36Sopenharmony_ci		return ret;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* Turning off */
90062306a36Sopenharmony_ci	if (!fb)
90162306a36Sopenharmony_ci		return 0;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* A lot of the code assumes this */
90462306a36Sopenharmony_ci	if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
90562306a36Sopenharmony_ci		DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
90662306a36Sopenharmony_ci			  new_state->crtc_w, new_state->crtc_h);
90762306a36Sopenharmony_ci		return -EINVAL;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (!vmw_framebuffer_to_vfb(fb)->bo) {
91162306a36Sopenharmony_ci		surface = vmw_framebuffer_to_vfbs(fb)->surface;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		WARN_ON(!surface);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (!surface ||
91662306a36Sopenharmony_ci		    (!surface->snooper.image && !surface->res.guest_memory_bo)) {
91762306a36Sopenharmony_ci			DRM_ERROR("surface not suitable for cursor\n");
91862306a36Sopenharmony_ci			return -EINVAL;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return 0;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ciint vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
92762306a36Sopenharmony_ci			     struct drm_atomic_state *state)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state,
93062306a36Sopenharmony_ci									 crtc);
93162306a36Sopenharmony_ci	struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc);
93262306a36Sopenharmony_ci	int connector_mask = drm_connector_mask(&du->connector);
93362306a36Sopenharmony_ci	bool has_primary = new_state->plane_mask &
93462306a36Sopenharmony_ci			   drm_plane_mask(crtc->primary);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* We always want to have an active plane with an active CRTC */
93762306a36Sopenharmony_ci	if (has_primary != new_state->enable)
93862306a36Sopenharmony_ci		return -EINVAL;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (new_state->connector_mask != connector_mask &&
94262306a36Sopenharmony_ci	    new_state->connector_mask != 0) {
94362306a36Sopenharmony_ci		DRM_ERROR("Invalid connectors configuration\n");
94462306a36Sopenharmony_ci		return -EINVAL;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/*
94862306a36Sopenharmony_ci	 * Our virtual device does not have a dot clock, so use the logical
94962306a36Sopenharmony_ci	 * clock value as the dot clock.
95062306a36Sopenharmony_ci	 */
95162306a36Sopenharmony_ci	if (new_state->mode.crtc_clock == 0)
95262306a36Sopenharmony_ci		new_state->adjusted_mode.crtc_clock = new_state->mode.clock;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_civoid vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
95962306a36Sopenharmony_ci			      struct drm_atomic_state *state)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_civoid vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
96562306a36Sopenharmony_ci			      struct drm_atomic_state *state)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci/**
97162306a36Sopenharmony_ci * vmw_du_crtc_duplicate_state - duplicate crtc state
97262306a36Sopenharmony_ci * @crtc: DRM crtc
97362306a36Sopenharmony_ci *
97462306a36Sopenharmony_ci * Allocates and returns a copy of the crtc state (both common and
97562306a36Sopenharmony_ci * vmw-specific) for the specified crtc.
97662306a36Sopenharmony_ci *
97762306a36Sopenharmony_ci * Returns: The newly allocated crtc state, or NULL on failure.
97862306a36Sopenharmony_ci */
97962306a36Sopenharmony_cistruct drm_crtc_state *
98062306a36Sopenharmony_civmw_du_crtc_duplicate_state(struct drm_crtc *crtc)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct drm_crtc_state *state;
98362306a36Sopenharmony_ci	struct vmw_crtc_state *vcs;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (WARN_ON(!crtc->state))
98662306a36Sopenharmony_ci		return NULL;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (!vcs)
99162306a36Sopenharmony_ci		return NULL;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	state = &vcs->base;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, state);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return state;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci/**
100262306a36Sopenharmony_ci * vmw_du_crtc_reset - creates a blank vmw crtc state
100362306a36Sopenharmony_ci * @crtc: DRM crtc
100462306a36Sopenharmony_ci *
100562306a36Sopenharmony_ci * Resets the atomic state for @crtc by freeing the state pointer (which
100662306a36Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state
100762306a36Sopenharmony_ci * object.
100862306a36Sopenharmony_ci */
100962306a36Sopenharmony_civoid vmw_du_crtc_reset(struct drm_crtc *crtc)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct vmw_crtc_state *vcs;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (crtc->state) {
101562306a36Sopenharmony_ci		__drm_atomic_helper_crtc_destroy_state(crtc->state);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		kfree(vmw_crtc_state_to_vcs(crtc->state));
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	vcs = kzalloc(sizeof(*vcs), GFP_KERNEL);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (!vcs) {
102362306a36Sopenharmony_ci		DRM_ERROR("Cannot allocate vmw_crtc_state\n");
102462306a36Sopenharmony_ci		return;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	__drm_atomic_helper_crtc_reset(crtc, &vcs->base);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci/**
103262306a36Sopenharmony_ci * vmw_du_crtc_destroy_state - destroy crtc state
103362306a36Sopenharmony_ci * @crtc: DRM crtc
103462306a36Sopenharmony_ci * @state: state object to destroy
103562306a36Sopenharmony_ci *
103662306a36Sopenharmony_ci * Destroys the crtc state (both common and vmw-specific) for the
103762306a36Sopenharmony_ci * specified plane.
103862306a36Sopenharmony_ci */
103962306a36Sopenharmony_civoid
104062306a36Sopenharmony_civmw_du_crtc_destroy_state(struct drm_crtc *crtc,
104162306a36Sopenharmony_ci			  struct drm_crtc_state *state)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	drm_atomic_helper_crtc_destroy_state(crtc, state);
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci/**
104862306a36Sopenharmony_ci * vmw_du_plane_duplicate_state - duplicate plane state
104962306a36Sopenharmony_ci * @plane: drm plane
105062306a36Sopenharmony_ci *
105162306a36Sopenharmony_ci * Allocates and returns a copy of the plane state (both common and
105262306a36Sopenharmony_ci * vmw-specific) for the specified plane.
105362306a36Sopenharmony_ci *
105462306a36Sopenharmony_ci * Returns: The newly allocated plane state, or NULL on failure.
105562306a36Sopenharmony_ci */
105662306a36Sopenharmony_cistruct drm_plane_state *
105762306a36Sopenharmony_civmw_du_plane_duplicate_state(struct drm_plane *plane)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct drm_plane_state *state;
106062306a36Sopenharmony_ci	struct vmw_plane_state *vps;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (!vps)
106562306a36Sopenharmony_ci		return NULL;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	vps->pinned = 0;
106862306a36Sopenharmony_ci	vps->cpp = 0;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	memset(&vps->cursor, 0, sizeof(vps->cursor));
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* Each ref counted resource needs to be acquired again */
107362306a36Sopenharmony_ci	if (vps->surf)
107462306a36Sopenharmony_ci		(void) vmw_surface_reference(vps->surf);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (vps->bo)
107762306a36Sopenharmony_ci		(void) vmw_bo_reference(vps->bo);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	state = &vps->base;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	__drm_atomic_helper_plane_duplicate_state(plane, state);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	return state;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci/**
108862306a36Sopenharmony_ci * vmw_du_plane_reset - creates a blank vmw plane state
108962306a36Sopenharmony_ci * @plane: drm plane
109062306a36Sopenharmony_ci *
109162306a36Sopenharmony_ci * Resets the atomic state for @plane by freeing the state pointer (which might
109262306a36Sopenharmony_ci * be NULL, e.g. at driver load time) and allocating a new empty state object.
109362306a36Sopenharmony_ci */
109462306a36Sopenharmony_civoid vmw_du_plane_reset(struct drm_plane *plane)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	struct vmw_plane_state *vps;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (plane->state)
109962306a36Sopenharmony_ci		vmw_du_plane_destroy_state(plane, plane->state);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	vps = kzalloc(sizeof(*vps), GFP_KERNEL);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (!vps) {
110462306a36Sopenharmony_ci		DRM_ERROR("Cannot allocate vmw_plane_state\n");
110562306a36Sopenharmony_ci		return;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	__drm_atomic_helper_plane_reset(plane, &vps->base);
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci/**
111362306a36Sopenharmony_ci * vmw_du_plane_destroy_state - destroy plane state
111462306a36Sopenharmony_ci * @plane: DRM plane
111562306a36Sopenharmony_ci * @state: state object to destroy
111662306a36Sopenharmony_ci *
111762306a36Sopenharmony_ci * Destroys the plane state (both common and vmw-specific) for the
111862306a36Sopenharmony_ci * specified plane.
111962306a36Sopenharmony_ci */
112062306a36Sopenharmony_civoid
112162306a36Sopenharmony_civmw_du_plane_destroy_state(struct drm_plane *plane,
112262306a36Sopenharmony_ci			   struct drm_plane_state *state)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct vmw_plane_state *vps = vmw_plane_state_to_vps(state);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* Should have been freed by cleanup_fb */
112762306a36Sopenharmony_ci	if (vps->surf)
112862306a36Sopenharmony_ci		vmw_surface_unreference(&vps->surf);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (vps->bo)
113162306a36Sopenharmony_ci		vmw_bo_unreference(&vps->bo);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	drm_atomic_helper_plane_destroy_state(plane, state);
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/**
113862306a36Sopenharmony_ci * vmw_du_connector_duplicate_state - duplicate connector state
113962306a36Sopenharmony_ci * @connector: DRM connector
114062306a36Sopenharmony_ci *
114162306a36Sopenharmony_ci * Allocates and returns a copy of the connector state (both common and
114262306a36Sopenharmony_ci * vmw-specific) for the specified connector.
114362306a36Sopenharmony_ci *
114462306a36Sopenharmony_ci * Returns: The newly allocated connector state, or NULL on failure.
114562306a36Sopenharmony_ci */
114662306a36Sopenharmony_cistruct drm_connector_state *
114762306a36Sopenharmony_civmw_du_connector_duplicate_state(struct drm_connector *connector)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	struct drm_connector_state *state;
115062306a36Sopenharmony_ci	struct vmw_connector_state *vcs;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	if (WARN_ON(!connector->state))
115362306a36Sopenharmony_ci		return NULL;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (!vcs)
115862306a36Sopenharmony_ci		return NULL;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	state = &vcs->base;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	__drm_atomic_helper_connector_duplicate_state(connector, state);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return state;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/**
116962306a36Sopenharmony_ci * vmw_du_connector_reset - creates a blank vmw connector state
117062306a36Sopenharmony_ci * @connector: DRM connector
117162306a36Sopenharmony_ci *
117262306a36Sopenharmony_ci * Resets the atomic state for @connector by freeing the state pointer (which
117362306a36Sopenharmony_ci * might be NULL, e.g. at driver load time) and allocating a new empty state
117462306a36Sopenharmony_ci * object.
117562306a36Sopenharmony_ci */
117662306a36Sopenharmony_civoid vmw_du_connector_reset(struct drm_connector *connector)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	struct vmw_connector_state *vcs;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (connector->state) {
118262306a36Sopenharmony_ci		__drm_atomic_helper_connector_destroy_state(connector->state);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		kfree(vmw_connector_state_to_vcs(connector->state));
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	vcs = kzalloc(sizeof(*vcs), GFP_KERNEL);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (!vcs) {
119062306a36Sopenharmony_ci		DRM_ERROR("Cannot allocate vmw_connector_state\n");
119162306a36Sopenharmony_ci		return;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	__drm_atomic_helper_connector_reset(connector, &vcs->base);
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci/**
119962306a36Sopenharmony_ci * vmw_du_connector_destroy_state - destroy connector state
120062306a36Sopenharmony_ci * @connector: DRM connector
120162306a36Sopenharmony_ci * @state: state object to destroy
120262306a36Sopenharmony_ci *
120362306a36Sopenharmony_ci * Destroys the connector state (both common and vmw-specific) for the
120462306a36Sopenharmony_ci * specified plane.
120562306a36Sopenharmony_ci */
120662306a36Sopenharmony_civoid
120762306a36Sopenharmony_civmw_du_connector_destroy_state(struct drm_connector *connector,
120862306a36Sopenharmony_ci			  struct drm_connector_state *state)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	drm_atomic_helper_connector_destroy_state(connector, state);
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci/*
121362306a36Sopenharmony_ci * Generic framebuffer code
121462306a36Sopenharmony_ci */
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci/*
121762306a36Sopenharmony_ci * Surface framebuffer code
121862306a36Sopenharmony_ci */
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct vmw_framebuffer_surface *vfbs =
122362306a36Sopenharmony_ci		vmw_framebuffer_to_vfbs(framebuffer);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	drm_framebuffer_cleanup(framebuffer);
122662306a36Sopenharmony_ci	vmw_surface_unreference(&vfbs->surface);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	kfree(vfbs);
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/**
123262306a36Sopenharmony_ci * vmw_kms_readback - Perform a readback from the screen system to
123362306a36Sopenharmony_ci * a buffer-object backed framebuffer.
123462306a36Sopenharmony_ci *
123562306a36Sopenharmony_ci * @dev_priv: Pointer to the device private structure.
123662306a36Sopenharmony_ci * @file_priv: Pointer to a struct drm_file identifying the caller.
123762306a36Sopenharmony_ci * Must be set to NULL if @user_fence_rep is NULL.
123862306a36Sopenharmony_ci * @vfb: Pointer to the buffer-object backed framebuffer.
123962306a36Sopenharmony_ci * @user_fence_rep: User-space provided structure for fence information.
124062306a36Sopenharmony_ci * Must be set to non-NULL if @file_priv is non-NULL.
124162306a36Sopenharmony_ci * @vclips: Array of clip rects.
124262306a36Sopenharmony_ci * @num_clips: Number of clip rects in @vclips.
124362306a36Sopenharmony_ci *
124462306a36Sopenharmony_ci * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
124562306a36Sopenharmony_ci * interrupted.
124662306a36Sopenharmony_ci */
124762306a36Sopenharmony_ciint vmw_kms_readback(struct vmw_private *dev_priv,
124862306a36Sopenharmony_ci		     struct drm_file *file_priv,
124962306a36Sopenharmony_ci		     struct vmw_framebuffer *vfb,
125062306a36Sopenharmony_ci		     struct drm_vmw_fence_rep __user *user_fence_rep,
125162306a36Sopenharmony_ci		     struct drm_vmw_rect *vclips,
125262306a36Sopenharmony_ci		     uint32_t num_clips)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	switch (dev_priv->active_display_unit) {
125562306a36Sopenharmony_ci	case vmw_du_screen_object:
125662306a36Sopenharmony_ci		return vmw_kms_sou_readback(dev_priv, file_priv, vfb,
125762306a36Sopenharmony_ci					    user_fence_rep, vclips, num_clips,
125862306a36Sopenharmony_ci					    NULL);
125962306a36Sopenharmony_ci	case vmw_du_screen_target:
126062306a36Sopenharmony_ci		return vmw_kms_stdu_readback(dev_priv, file_priv, vfb,
126162306a36Sopenharmony_ci					     user_fence_rep, NULL, vclips, num_clips,
126262306a36Sopenharmony_ci					     1, NULL);
126362306a36Sopenharmony_ci	default:
126462306a36Sopenharmony_ci		WARN_ONCE(true,
126562306a36Sopenharmony_ci			  "Readback called with invalid display system.\n");
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	return -ENOSYS;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
127362306a36Sopenharmony_ci	.destroy = vmw_framebuffer_surface_destroy,
127462306a36Sopenharmony_ci	.dirty = drm_atomic_helper_dirtyfb,
127562306a36Sopenharmony_ci};
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
127862306a36Sopenharmony_ci					   struct vmw_surface *surface,
127962306a36Sopenharmony_ci					   struct vmw_framebuffer **out,
128062306a36Sopenharmony_ci					   const struct drm_mode_fb_cmd2
128162306a36Sopenharmony_ci					   *mode_cmd,
128262306a36Sopenharmony_ci					   bool is_bo_proxy)
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
128662306a36Sopenharmony_ci	struct vmw_framebuffer_surface *vfbs;
128762306a36Sopenharmony_ci	enum SVGA3dSurfaceFormat format;
128862306a36Sopenharmony_ci	int ret;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	/* 3D is only supported on HWv8 and newer hosts */
129162306a36Sopenharmony_ci	if (dev_priv->active_display_unit == vmw_du_legacy)
129262306a36Sopenharmony_ci		return -ENOSYS;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/*
129562306a36Sopenharmony_ci	 * Sanity checks.
129662306a36Sopenharmony_ci	 */
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (!drm_any_plane_has_format(&dev_priv->drm,
129962306a36Sopenharmony_ci				      mode_cmd->pixel_format,
130062306a36Sopenharmony_ci				      mode_cmd->modifier[0])) {
130162306a36Sopenharmony_ci		drm_dbg(&dev_priv->drm,
130262306a36Sopenharmony_ci			"unsupported pixel format %p4cc / modifier 0x%llx\n",
130362306a36Sopenharmony_ci			&mode_cmd->pixel_format, mode_cmd->modifier[0]);
130462306a36Sopenharmony_ci		return -EINVAL;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* Surface must be marked as a scanout. */
130862306a36Sopenharmony_ci	if (unlikely(!surface->metadata.scanout))
130962306a36Sopenharmony_ci		return -EINVAL;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (unlikely(surface->metadata.mip_levels[0] != 1 ||
131262306a36Sopenharmony_ci		     surface->metadata.num_sizes != 1 ||
131362306a36Sopenharmony_ci		     surface->metadata.base_size.width < mode_cmd->width ||
131462306a36Sopenharmony_ci		     surface->metadata.base_size.height < mode_cmd->height ||
131562306a36Sopenharmony_ci		     surface->metadata.base_size.depth != 1)) {
131662306a36Sopenharmony_ci		DRM_ERROR("Incompatible surface dimensions "
131762306a36Sopenharmony_ci			  "for requested mode.\n");
131862306a36Sopenharmony_ci		return -EINVAL;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	switch (mode_cmd->pixel_format) {
132262306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
132362306a36Sopenharmony_ci		format = SVGA3D_A8R8G8B8;
132462306a36Sopenharmony_ci		break;
132562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
132662306a36Sopenharmony_ci		format = SVGA3D_X8R8G8B8;
132762306a36Sopenharmony_ci		break;
132862306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
132962306a36Sopenharmony_ci		format = SVGA3D_R5G6B5;
133062306a36Sopenharmony_ci		break;
133162306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
133262306a36Sopenharmony_ci		format = SVGA3D_A1R5G5B5;
133362306a36Sopenharmony_ci		break;
133462306a36Sopenharmony_ci	default:
133562306a36Sopenharmony_ci		DRM_ERROR("Invalid pixel format: %p4cc\n",
133662306a36Sopenharmony_ci			  &mode_cmd->pixel_format);
133762306a36Sopenharmony_ci		return -EINVAL;
133862306a36Sopenharmony_ci	}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	/*
134162306a36Sopenharmony_ci	 * For DX, surface format validation is done when surface->scanout
134262306a36Sopenharmony_ci	 * is set.
134362306a36Sopenharmony_ci	 */
134462306a36Sopenharmony_ci	if (!has_sm4_context(dev_priv) && format != surface->metadata.format) {
134562306a36Sopenharmony_ci		DRM_ERROR("Invalid surface format for requested mode.\n");
134662306a36Sopenharmony_ci		return -EINVAL;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
135062306a36Sopenharmony_ci	if (!vfbs) {
135162306a36Sopenharmony_ci		ret = -ENOMEM;
135262306a36Sopenharmony_ci		goto out_err1;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd);
135662306a36Sopenharmony_ci	vfbs->surface = vmw_surface_reference(surface);
135762306a36Sopenharmony_ci	vfbs->base.user_handle = mode_cmd->handles[0];
135862306a36Sopenharmony_ci	vfbs->is_bo_proxy = is_bo_proxy;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	*out = &vfbs->base;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	ret = drm_framebuffer_init(dev, &vfbs->base.base,
136362306a36Sopenharmony_ci				   &vmw_framebuffer_surface_funcs);
136462306a36Sopenharmony_ci	if (ret)
136562306a36Sopenharmony_ci		goto out_err2;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	return 0;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ciout_err2:
137062306a36Sopenharmony_ci	vmw_surface_unreference(&surface);
137162306a36Sopenharmony_ci	kfree(vfbs);
137262306a36Sopenharmony_ciout_err1:
137362306a36Sopenharmony_ci	return ret;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci/*
137762306a36Sopenharmony_ci * Buffer-object framebuffer code
137862306a36Sopenharmony_ci */
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic int vmw_framebuffer_bo_create_handle(struct drm_framebuffer *fb,
138162306a36Sopenharmony_ci					    struct drm_file *file_priv,
138262306a36Sopenharmony_ci					    unsigned int *handle)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct vmw_framebuffer_bo *vfbd =
138562306a36Sopenharmony_ci			vmw_framebuffer_to_vfbd(fb);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	return drm_gem_handle_create(file_priv, &vfbd->buffer->tbo.base, handle);
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct vmw_framebuffer_bo *vfbd =
139362306a36Sopenharmony_ci		vmw_framebuffer_to_vfbd(framebuffer);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	drm_framebuffer_cleanup(framebuffer);
139662306a36Sopenharmony_ci	vmw_bo_unreference(&vfbd->buffer);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	kfree(vfbd);
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = {
140262306a36Sopenharmony_ci	.create_handle = vmw_framebuffer_bo_create_handle,
140362306a36Sopenharmony_ci	.destroy = vmw_framebuffer_bo_destroy,
140462306a36Sopenharmony_ci	.dirty = drm_atomic_helper_dirtyfb,
140562306a36Sopenharmony_ci};
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci/**
140862306a36Sopenharmony_ci * vmw_create_bo_proxy - create a proxy surface for the buffer object
140962306a36Sopenharmony_ci *
141062306a36Sopenharmony_ci * @dev: DRM device
141162306a36Sopenharmony_ci * @mode_cmd: parameters for the new surface
141262306a36Sopenharmony_ci * @bo_mob: MOB backing the buffer object
141362306a36Sopenharmony_ci * @srf_out: newly created surface
141462306a36Sopenharmony_ci *
141562306a36Sopenharmony_ci * When the content FB is a buffer object, we create a surface as a proxy to the
141662306a36Sopenharmony_ci * same buffer.  This way we can do a surface copy rather than a surface DMA.
141762306a36Sopenharmony_ci * This is a more efficient approach
141862306a36Sopenharmony_ci *
141962306a36Sopenharmony_ci * RETURNS:
142062306a36Sopenharmony_ci * 0 on success, error code otherwise
142162306a36Sopenharmony_ci */
142262306a36Sopenharmony_cistatic int vmw_create_bo_proxy(struct drm_device *dev,
142362306a36Sopenharmony_ci			       const struct drm_mode_fb_cmd2 *mode_cmd,
142462306a36Sopenharmony_ci			       struct vmw_bo *bo_mob,
142562306a36Sopenharmony_ci			       struct vmw_surface **srf_out)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct vmw_surface_metadata metadata = {0};
142862306a36Sopenharmony_ci	uint32_t format;
142962306a36Sopenharmony_ci	struct vmw_resource *res;
143062306a36Sopenharmony_ci	unsigned int bytes_pp;
143162306a36Sopenharmony_ci	int ret;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	switch (mode_cmd->pixel_format) {
143462306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
143562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
143662306a36Sopenharmony_ci		format = SVGA3D_X8R8G8B8;
143762306a36Sopenharmony_ci		bytes_pp = 4;
143862306a36Sopenharmony_ci		break;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
144162306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
144262306a36Sopenharmony_ci		format = SVGA3D_R5G6B5;
144362306a36Sopenharmony_ci		bytes_pp = 2;
144462306a36Sopenharmony_ci		break;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	case 8:
144762306a36Sopenharmony_ci		format = SVGA3D_P8;
144862306a36Sopenharmony_ci		bytes_pp = 1;
144962306a36Sopenharmony_ci		break;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	default:
145262306a36Sopenharmony_ci		DRM_ERROR("Invalid framebuffer format %p4cc\n",
145362306a36Sopenharmony_ci			  &mode_cmd->pixel_format);
145462306a36Sopenharmony_ci		return -EINVAL;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	metadata.format = format;
145862306a36Sopenharmony_ci	metadata.mip_levels[0] = 1;
145962306a36Sopenharmony_ci	metadata.num_sizes = 1;
146062306a36Sopenharmony_ci	metadata.base_size.width = mode_cmd->pitches[0] / bytes_pp;
146162306a36Sopenharmony_ci	metadata.base_size.height =  mode_cmd->height;
146262306a36Sopenharmony_ci	metadata.base_size.depth = 1;
146362306a36Sopenharmony_ci	metadata.scanout = true;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	ret = vmw_gb_surface_define(vmw_priv(dev), &metadata, srf_out);
146662306a36Sopenharmony_ci	if (ret) {
146762306a36Sopenharmony_ci		DRM_ERROR("Failed to allocate proxy content buffer\n");
146862306a36Sopenharmony_ci		return ret;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	res = &(*srf_out)->res;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/* Reserve and switch the backing mob. */
147462306a36Sopenharmony_ci	mutex_lock(&res->dev_priv->cmdbuf_mutex);
147562306a36Sopenharmony_ci	(void) vmw_resource_reserve(res, false, true);
147662306a36Sopenharmony_ci	vmw_user_bo_unref(&res->guest_memory_bo);
147762306a36Sopenharmony_ci	res->guest_memory_bo = vmw_user_bo_ref(bo_mob);
147862306a36Sopenharmony_ci	res->guest_memory_offset = 0;
147962306a36Sopenharmony_ci	vmw_resource_unreserve(res, false, false, false, NULL, 0);
148062306a36Sopenharmony_ci	mutex_unlock(&res->dev_priv->cmdbuf_mutex);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	return 0;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistatic int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv,
148862306a36Sopenharmony_ci				      struct vmw_bo *bo,
148962306a36Sopenharmony_ci				      struct vmw_framebuffer **out,
149062306a36Sopenharmony_ci				      const struct drm_mode_fb_cmd2
149162306a36Sopenharmony_ci				      *mode_cmd)
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
149562306a36Sopenharmony_ci	struct vmw_framebuffer_bo *vfbd;
149662306a36Sopenharmony_ci	unsigned int requested_size;
149762306a36Sopenharmony_ci	int ret;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	requested_size = mode_cmd->height * mode_cmd->pitches[0];
150062306a36Sopenharmony_ci	if (unlikely(requested_size > bo->tbo.base.size)) {
150162306a36Sopenharmony_ci		DRM_ERROR("Screen buffer object size is too small "
150262306a36Sopenharmony_ci			  "for requested mode.\n");
150362306a36Sopenharmony_ci		return -EINVAL;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (!drm_any_plane_has_format(&dev_priv->drm,
150762306a36Sopenharmony_ci				      mode_cmd->pixel_format,
150862306a36Sopenharmony_ci				      mode_cmd->modifier[0])) {
150962306a36Sopenharmony_ci		drm_dbg(&dev_priv->drm,
151062306a36Sopenharmony_ci			"unsupported pixel format %p4cc / modifier 0x%llx\n",
151162306a36Sopenharmony_ci			&mode_cmd->pixel_format, mode_cmd->modifier[0]);
151262306a36Sopenharmony_ci		return -EINVAL;
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
151662306a36Sopenharmony_ci	if (!vfbd) {
151762306a36Sopenharmony_ci		ret = -ENOMEM;
151862306a36Sopenharmony_ci		goto out_err1;
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	vfbd->base.base.obj[0] = &bo->tbo.base;
152262306a36Sopenharmony_ci	drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd);
152362306a36Sopenharmony_ci	vfbd->base.bo = true;
152462306a36Sopenharmony_ci	vfbd->buffer = vmw_bo_reference(bo);
152562306a36Sopenharmony_ci	vfbd->base.user_handle = mode_cmd->handles[0];
152662306a36Sopenharmony_ci	*out = &vfbd->base;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	ret = drm_framebuffer_init(dev, &vfbd->base.base,
152962306a36Sopenharmony_ci				   &vmw_framebuffer_bo_funcs);
153062306a36Sopenharmony_ci	if (ret)
153162306a36Sopenharmony_ci		goto out_err2;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	return 0;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ciout_err2:
153662306a36Sopenharmony_ci	vmw_bo_unreference(&bo);
153762306a36Sopenharmony_ci	kfree(vfbd);
153862306a36Sopenharmony_ciout_err1:
153962306a36Sopenharmony_ci	return ret;
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci/**
154462306a36Sopenharmony_ci * vmw_kms_srf_ok - check if a surface can be created
154562306a36Sopenharmony_ci *
154662306a36Sopenharmony_ci * @dev_priv: Pointer to device private struct.
154762306a36Sopenharmony_ci * @width: requested width
154862306a36Sopenharmony_ci * @height: requested height
154962306a36Sopenharmony_ci *
155062306a36Sopenharmony_ci * Surfaces need to be less than texture size
155162306a36Sopenharmony_ci */
155262306a36Sopenharmony_cistatic bool
155362306a36Sopenharmony_civmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	if (width  > dev_priv->texture_max_width ||
155662306a36Sopenharmony_ci	    height > dev_priv->texture_max_height)
155762306a36Sopenharmony_ci		return false;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	return true;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci/**
156362306a36Sopenharmony_ci * vmw_kms_new_framebuffer - Create a new framebuffer.
156462306a36Sopenharmony_ci *
156562306a36Sopenharmony_ci * @dev_priv: Pointer to device private struct.
156662306a36Sopenharmony_ci * @bo: Pointer to buffer object to wrap the kms framebuffer around.
156762306a36Sopenharmony_ci * Either @bo or @surface must be NULL.
156862306a36Sopenharmony_ci * @surface: Pointer to a surface to wrap the kms framebuffer around.
156962306a36Sopenharmony_ci * Either @bo or @surface must be NULL.
157062306a36Sopenharmony_ci * @only_2d: No presents will occur to this buffer object based framebuffer.
157162306a36Sopenharmony_ci * This helps the code to do some important optimizations.
157262306a36Sopenharmony_ci * @mode_cmd: Frame-buffer metadata.
157362306a36Sopenharmony_ci */
157462306a36Sopenharmony_cistruct vmw_framebuffer *
157562306a36Sopenharmony_civmw_kms_new_framebuffer(struct vmw_private *dev_priv,
157662306a36Sopenharmony_ci			struct vmw_bo *bo,
157762306a36Sopenharmony_ci			struct vmw_surface *surface,
157862306a36Sopenharmony_ci			bool only_2d,
157962306a36Sopenharmony_ci			const struct drm_mode_fb_cmd2 *mode_cmd)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct vmw_framebuffer *vfb = NULL;
158262306a36Sopenharmony_ci	bool is_bo_proxy = false;
158362306a36Sopenharmony_ci	int ret;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	/*
158662306a36Sopenharmony_ci	 * We cannot use the SurfaceDMA command in an non-accelerated VM,
158762306a36Sopenharmony_ci	 * therefore, wrap the buffer object in a surface so we can use the
158862306a36Sopenharmony_ci	 * SurfaceCopy command.
158962306a36Sopenharmony_ci	 */
159062306a36Sopenharmony_ci	if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)  &&
159162306a36Sopenharmony_ci	    bo && only_2d &&
159262306a36Sopenharmony_ci	    mode_cmd->width > 64 &&  /* Don't create a proxy for cursor */
159362306a36Sopenharmony_ci	    dev_priv->active_display_unit == vmw_du_screen_target) {
159462306a36Sopenharmony_ci		ret = vmw_create_bo_proxy(&dev_priv->drm, mode_cmd,
159562306a36Sopenharmony_ci					  bo, &surface);
159662306a36Sopenharmony_ci		if (ret)
159762306a36Sopenharmony_ci			return ERR_PTR(ret);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci		is_bo_proxy = true;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	/* Create the new framebuffer depending one what we have */
160362306a36Sopenharmony_ci	if (surface) {
160462306a36Sopenharmony_ci		ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
160562306a36Sopenharmony_ci						      mode_cmd,
160662306a36Sopenharmony_ci						      is_bo_proxy);
160762306a36Sopenharmony_ci		/*
160862306a36Sopenharmony_ci		 * vmw_create_bo_proxy() adds a reference that is no longer
160962306a36Sopenharmony_ci		 * needed
161062306a36Sopenharmony_ci		 */
161162306a36Sopenharmony_ci		if (is_bo_proxy)
161262306a36Sopenharmony_ci			vmw_surface_unreference(&surface);
161362306a36Sopenharmony_ci	} else if (bo) {
161462306a36Sopenharmony_ci		ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb,
161562306a36Sopenharmony_ci						 mode_cmd);
161662306a36Sopenharmony_ci	} else {
161762306a36Sopenharmony_ci		BUG();
161862306a36Sopenharmony_ci	}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (ret)
162162306a36Sopenharmony_ci		return ERR_PTR(ret);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	return vfb;
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci/*
162762306a36Sopenharmony_ci * Generic Kernel modesetting functions
162862306a36Sopenharmony_ci */
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_cistatic struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
163162306a36Sopenharmony_ci						 struct drm_file *file_priv,
163262306a36Sopenharmony_ci						 const struct drm_mode_fb_cmd2 *mode_cmd)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
163562306a36Sopenharmony_ci	struct vmw_framebuffer *vfb = NULL;
163662306a36Sopenharmony_ci	struct vmw_surface *surface = NULL;
163762306a36Sopenharmony_ci	struct vmw_bo *bo = NULL;
163862306a36Sopenharmony_ci	int ret;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/* returns either a bo or surface */
164162306a36Sopenharmony_ci	ret = vmw_user_lookup_handle(dev_priv, file_priv,
164262306a36Sopenharmony_ci				     mode_cmd->handles[0],
164362306a36Sopenharmony_ci				     &surface, &bo);
164462306a36Sopenharmony_ci	if (ret) {
164562306a36Sopenharmony_ci		DRM_ERROR("Invalid buffer object handle %u (0x%x).\n",
164662306a36Sopenharmony_ci			  mode_cmd->handles[0], mode_cmd->handles[0]);
164762306a36Sopenharmony_ci		goto err_out;
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	if (!bo &&
165262306a36Sopenharmony_ci	    !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) {
165362306a36Sopenharmony_ci		DRM_ERROR("Surface size cannot exceed %dx%d\n",
165462306a36Sopenharmony_ci			dev_priv->texture_max_width,
165562306a36Sopenharmony_ci			dev_priv->texture_max_height);
165662306a36Sopenharmony_ci		goto err_out;
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface,
166162306a36Sopenharmony_ci				      !(dev_priv->capabilities & SVGA_CAP_3D),
166262306a36Sopenharmony_ci				      mode_cmd);
166362306a36Sopenharmony_ci	if (IS_ERR(vfb)) {
166462306a36Sopenharmony_ci		ret = PTR_ERR(vfb);
166562306a36Sopenharmony_ci		goto err_out;
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cierr_out:
166962306a36Sopenharmony_ci	/* vmw_user_lookup_handle takes one ref so does new_fb */
167062306a36Sopenharmony_ci	if (bo)
167162306a36Sopenharmony_ci		vmw_user_bo_unref(&bo);
167262306a36Sopenharmony_ci	if (surface)
167362306a36Sopenharmony_ci		vmw_surface_unreference(&surface);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (ret) {
167662306a36Sopenharmony_ci		DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
167762306a36Sopenharmony_ci		return ERR_PTR(ret);
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	return &vfb->base;
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci/**
168462306a36Sopenharmony_ci * vmw_kms_check_display_memory - Validates display memory required for a
168562306a36Sopenharmony_ci * topology
168662306a36Sopenharmony_ci * @dev: DRM device
168762306a36Sopenharmony_ci * @num_rects: number of drm_rect in rects
168862306a36Sopenharmony_ci * @rects: array of drm_rect representing the topology to validate indexed by
168962306a36Sopenharmony_ci * crtc index.
169062306a36Sopenharmony_ci *
169162306a36Sopenharmony_ci * Returns:
169262306a36Sopenharmony_ci * 0 on success otherwise negative error code
169362306a36Sopenharmony_ci */
169462306a36Sopenharmony_cistatic int vmw_kms_check_display_memory(struct drm_device *dev,
169562306a36Sopenharmony_ci					uint32_t num_rects,
169662306a36Sopenharmony_ci					struct drm_rect *rects)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
169962306a36Sopenharmony_ci	struct drm_rect bounding_box = {0};
170062306a36Sopenharmony_ci	u64 total_pixels = 0, pixel_mem, bb_mem;
170162306a36Sopenharmony_ci	int i;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	for (i = 0; i < num_rects; i++) {
170462306a36Sopenharmony_ci		/*
170562306a36Sopenharmony_ci		 * For STDU only individual screen (screen target) is limited by
170662306a36Sopenharmony_ci		 * SCREENTARGET_MAX_WIDTH/HEIGHT registers.
170762306a36Sopenharmony_ci		 */
170862306a36Sopenharmony_ci		if (dev_priv->active_display_unit == vmw_du_screen_target &&
170962306a36Sopenharmony_ci		    (drm_rect_width(&rects[i]) > dev_priv->stdu_max_width ||
171062306a36Sopenharmony_ci		     drm_rect_height(&rects[i]) > dev_priv->stdu_max_height)) {
171162306a36Sopenharmony_ci			VMW_DEBUG_KMS("Screen size not supported.\n");
171262306a36Sopenharmony_ci			return -EINVAL;
171362306a36Sopenharmony_ci		}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		/* Bounding box upper left is at (0,0). */
171662306a36Sopenharmony_ci		if (rects[i].x2 > bounding_box.x2)
171762306a36Sopenharmony_ci			bounding_box.x2 = rects[i].x2;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci		if (rects[i].y2 > bounding_box.y2)
172062306a36Sopenharmony_ci			bounding_box.y2 = rects[i].y2;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		total_pixels += (u64) drm_rect_width(&rects[i]) *
172362306a36Sopenharmony_ci			(u64) drm_rect_height(&rects[i]);
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* Virtual svga device primary limits are always in 32-bpp. */
172762306a36Sopenharmony_ci	pixel_mem = total_pixels * 4;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/*
173062306a36Sopenharmony_ci	 * For HV10 and below prim_bb_mem is vram size. When
173162306a36Sopenharmony_ci	 * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is
173262306a36Sopenharmony_ci	 * limit on primary bounding box
173362306a36Sopenharmony_ci	 */
173462306a36Sopenharmony_ci	if (pixel_mem > dev_priv->max_primary_mem) {
173562306a36Sopenharmony_ci		VMW_DEBUG_KMS("Combined output size too large.\n");
173662306a36Sopenharmony_ci		return -EINVAL;
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	/* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */
174062306a36Sopenharmony_ci	if (dev_priv->active_display_unit != vmw_du_screen_target ||
174162306a36Sopenharmony_ci	    !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) {
174262306a36Sopenharmony_ci		bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci		if (bb_mem > dev_priv->max_primary_mem) {
174562306a36Sopenharmony_ci			VMW_DEBUG_KMS("Topology is beyond supported limits.\n");
174662306a36Sopenharmony_ci			return -EINVAL;
174762306a36Sopenharmony_ci		}
174862306a36Sopenharmony_ci	}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	return 0;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci/**
175462306a36Sopenharmony_ci * vmw_crtc_state_and_lock - Return new or current crtc state with locked
175562306a36Sopenharmony_ci * crtc mutex
175662306a36Sopenharmony_ci * @state: The atomic state pointer containing the new atomic state
175762306a36Sopenharmony_ci * @crtc: The crtc
175862306a36Sopenharmony_ci *
175962306a36Sopenharmony_ci * This function returns the new crtc state if it's part of the state update.
176062306a36Sopenharmony_ci * Otherwise returns the current crtc state. It also makes sure that the
176162306a36Sopenharmony_ci * crtc mutex is locked.
176262306a36Sopenharmony_ci *
176362306a36Sopenharmony_ci * Returns: A valid crtc state pointer or NULL. It may also return a
176462306a36Sopenharmony_ci * pointer error, in particular -EDEADLK if locking needs to be rerun.
176562306a36Sopenharmony_ci */
176662306a36Sopenharmony_cistatic struct drm_crtc_state *
176762306a36Sopenharmony_civmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
177262306a36Sopenharmony_ci	if (crtc_state) {
177362306a36Sopenharmony_ci		lockdep_assert_held(&crtc->mutex.mutex.base);
177462306a36Sopenharmony_ci	} else {
177562306a36Sopenharmony_ci		int ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci		if (ret != 0 && ret != -EALREADY)
177862306a36Sopenharmony_ci			return ERR_PTR(ret);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci		crtc_state = crtc->state;
178162306a36Sopenharmony_ci	}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	return crtc_state;
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci/**
178762306a36Sopenharmony_ci * vmw_kms_check_implicit - Verify that all implicit display units scan out
178862306a36Sopenharmony_ci * from the same fb after the new state is committed.
178962306a36Sopenharmony_ci * @dev: The drm_device.
179062306a36Sopenharmony_ci * @state: The new state to be checked.
179162306a36Sopenharmony_ci *
179262306a36Sopenharmony_ci * Returns:
179362306a36Sopenharmony_ci *   Zero on success,
179462306a36Sopenharmony_ci *   -EINVAL on invalid state,
179562306a36Sopenharmony_ci *   -EDEADLK if modeset locking needs to be rerun.
179662306a36Sopenharmony_ci */
179762306a36Sopenharmony_cistatic int vmw_kms_check_implicit(struct drm_device *dev,
179862306a36Sopenharmony_ci				  struct drm_atomic_state *state)
179962306a36Sopenharmony_ci{
180062306a36Sopenharmony_ci	struct drm_framebuffer *implicit_fb = NULL;
180162306a36Sopenharmony_ci	struct drm_crtc *crtc;
180262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
180362306a36Sopenharmony_ci	struct drm_plane_state *plane_state;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	drm_for_each_crtc(crtc, dev) {
180662306a36Sopenharmony_ci		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		if (!du->is_implicit)
180962306a36Sopenharmony_ci			continue;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci		crtc_state = vmw_crtc_state_and_lock(state, crtc);
181262306a36Sopenharmony_ci		if (IS_ERR(crtc_state))
181362306a36Sopenharmony_ci			return PTR_ERR(crtc_state);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		if (!crtc_state || !crtc_state->enable)
181662306a36Sopenharmony_ci			continue;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		/*
181962306a36Sopenharmony_ci		 * Can't move primary planes across crtcs, so this is OK.
182062306a36Sopenharmony_ci		 * It also means we don't need to take the plane mutex.
182162306a36Sopenharmony_ci		 */
182262306a36Sopenharmony_ci		plane_state = du->primary.state;
182362306a36Sopenharmony_ci		if (plane_state->crtc != crtc)
182462306a36Sopenharmony_ci			continue;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		if (!implicit_fb)
182762306a36Sopenharmony_ci			implicit_fb = plane_state->fb;
182862306a36Sopenharmony_ci		else if (implicit_fb != plane_state->fb)
182962306a36Sopenharmony_ci			return -EINVAL;
183062306a36Sopenharmony_ci	}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	return 0;
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci/**
183662306a36Sopenharmony_ci * vmw_kms_check_topology - Validates topology in drm_atomic_state
183762306a36Sopenharmony_ci * @dev: DRM device
183862306a36Sopenharmony_ci * @state: the driver state object
183962306a36Sopenharmony_ci *
184062306a36Sopenharmony_ci * Returns:
184162306a36Sopenharmony_ci * 0 on success otherwise negative error code
184262306a36Sopenharmony_ci */
184362306a36Sopenharmony_cistatic int vmw_kms_check_topology(struct drm_device *dev,
184462306a36Sopenharmony_ci				  struct drm_atomic_state *state)
184562306a36Sopenharmony_ci{
184662306a36Sopenharmony_ci	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
184762306a36Sopenharmony_ci	struct drm_rect *rects;
184862306a36Sopenharmony_ci	struct drm_crtc *crtc;
184962306a36Sopenharmony_ci	uint32_t i;
185062306a36Sopenharmony_ci	int ret = 0;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect),
185362306a36Sopenharmony_ci			GFP_KERNEL);
185462306a36Sopenharmony_ci	if (!rects)
185562306a36Sopenharmony_ci		return -ENOMEM;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	drm_for_each_crtc(crtc, dev) {
185862306a36Sopenharmony_ci		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
185962306a36Sopenharmony_ci		struct drm_crtc_state *crtc_state;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci		i = drm_crtc_index(crtc);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci		crtc_state = vmw_crtc_state_and_lock(state, crtc);
186462306a36Sopenharmony_ci		if (IS_ERR(crtc_state)) {
186562306a36Sopenharmony_ci			ret = PTR_ERR(crtc_state);
186662306a36Sopenharmony_ci			goto clean;
186762306a36Sopenharmony_ci		}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci		if (!crtc_state)
187062306a36Sopenharmony_ci			continue;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci		if (crtc_state->enable) {
187362306a36Sopenharmony_ci			rects[i].x1 = du->gui_x;
187462306a36Sopenharmony_ci			rects[i].y1 = du->gui_y;
187562306a36Sopenharmony_ci			rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay;
187662306a36Sopenharmony_ci			rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay;
187762306a36Sopenharmony_ci		} else {
187862306a36Sopenharmony_ci			rects[i].x1 = 0;
187962306a36Sopenharmony_ci			rects[i].y1 = 0;
188062306a36Sopenharmony_ci			rects[i].x2 = 0;
188162306a36Sopenharmony_ci			rects[i].y2 = 0;
188262306a36Sopenharmony_ci		}
188362306a36Sopenharmony_ci	}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	/* Determine change to topology due to new atomic state */
188662306a36Sopenharmony_ci	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,
188762306a36Sopenharmony_ci				      new_crtc_state, i) {
188862306a36Sopenharmony_ci		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
188962306a36Sopenharmony_ci		struct drm_connector *connector;
189062306a36Sopenharmony_ci		struct drm_connector_state *conn_state;
189162306a36Sopenharmony_ci		struct vmw_connector_state *vmw_conn_state;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci		if (!du->pref_active && new_crtc_state->enable) {
189462306a36Sopenharmony_ci			VMW_DEBUG_KMS("Enabling a disabled display unit\n");
189562306a36Sopenharmony_ci			ret = -EINVAL;
189662306a36Sopenharmony_ci			goto clean;
189762306a36Sopenharmony_ci		}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		/*
190062306a36Sopenharmony_ci		 * For vmwgfx each crtc has only one connector attached and it
190162306a36Sopenharmony_ci		 * is not changed so don't really need to check the
190262306a36Sopenharmony_ci		 * crtc->connector_mask and iterate over it.
190362306a36Sopenharmony_ci		 */
190462306a36Sopenharmony_ci		connector = &du->connector;
190562306a36Sopenharmony_ci		conn_state = drm_atomic_get_connector_state(state, connector);
190662306a36Sopenharmony_ci		if (IS_ERR(conn_state)) {
190762306a36Sopenharmony_ci			ret = PTR_ERR(conn_state);
190862306a36Sopenharmony_ci			goto clean;
190962306a36Sopenharmony_ci		}
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci		vmw_conn_state = vmw_connector_state_to_vcs(conn_state);
191262306a36Sopenharmony_ci		vmw_conn_state->gui_x = du->gui_x;
191362306a36Sopenharmony_ci		vmw_conn_state->gui_y = du->gui_y;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc,
191762306a36Sopenharmony_ci					   rects);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ciclean:
192062306a36Sopenharmony_ci	kfree(rects);
192162306a36Sopenharmony_ci	return ret;
192262306a36Sopenharmony_ci}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci/**
192562306a36Sopenharmony_ci * vmw_kms_atomic_check_modeset- validate state object for modeset changes
192662306a36Sopenharmony_ci *
192762306a36Sopenharmony_ci * @dev: DRM device
192862306a36Sopenharmony_ci * @state: the driver state object
192962306a36Sopenharmony_ci *
193062306a36Sopenharmony_ci * This is a simple wrapper around drm_atomic_helper_check_modeset() for
193162306a36Sopenharmony_ci * us to assign a value to mode->crtc_clock so that
193262306a36Sopenharmony_ci * drm_calc_timestamping_constants() won't throw an error message
193362306a36Sopenharmony_ci *
193462306a36Sopenharmony_ci * Returns:
193562306a36Sopenharmony_ci * Zero for success or -errno
193662306a36Sopenharmony_ci */
193762306a36Sopenharmony_cistatic int
193862306a36Sopenharmony_civmw_kms_atomic_check_modeset(struct drm_device *dev,
193962306a36Sopenharmony_ci			     struct drm_atomic_state *state)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	struct drm_crtc *crtc;
194262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
194362306a36Sopenharmony_ci	bool need_modeset = false;
194462306a36Sopenharmony_ci	int i, ret;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	ret = drm_atomic_helper_check(dev, state);
194762306a36Sopenharmony_ci	if (ret)
194862306a36Sopenharmony_ci		return ret;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	ret = vmw_kms_check_implicit(dev, state);
195162306a36Sopenharmony_ci	if (ret) {
195262306a36Sopenharmony_ci		VMW_DEBUG_KMS("Invalid implicit state\n");
195362306a36Sopenharmony_ci		return ret;
195462306a36Sopenharmony_ci	}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
195762306a36Sopenharmony_ci		if (drm_atomic_crtc_needs_modeset(crtc_state))
195862306a36Sopenharmony_ci			need_modeset = true;
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	if (need_modeset)
196262306a36Sopenharmony_ci		return vmw_kms_check_topology(dev, state);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return ret;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic const struct drm_mode_config_funcs vmw_kms_funcs = {
196862306a36Sopenharmony_ci	.fb_create = vmw_kms_fb_create,
196962306a36Sopenharmony_ci	.atomic_check = vmw_kms_atomic_check_modeset,
197062306a36Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
197162306a36Sopenharmony_ci};
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_cistatic int vmw_kms_generic_present(struct vmw_private *dev_priv,
197462306a36Sopenharmony_ci				   struct drm_file *file_priv,
197562306a36Sopenharmony_ci				   struct vmw_framebuffer *vfb,
197662306a36Sopenharmony_ci				   struct vmw_surface *surface,
197762306a36Sopenharmony_ci				   uint32_t sid,
197862306a36Sopenharmony_ci				   int32_t destX, int32_t destY,
197962306a36Sopenharmony_ci				   struct drm_vmw_rect *clips,
198062306a36Sopenharmony_ci				   uint32_t num_clips)
198162306a36Sopenharmony_ci{
198262306a36Sopenharmony_ci	return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips,
198362306a36Sopenharmony_ci					    &surface->res, destX, destY,
198462306a36Sopenharmony_ci					    num_clips, 1, NULL, NULL);
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ciint vmw_kms_present(struct vmw_private *dev_priv,
198962306a36Sopenharmony_ci		    struct drm_file *file_priv,
199062306a36Sopenharmony_ci		    struct vmw_framebuffer *vfb,
199162306a36Sopenharmony_ci		    struct vmw_surface *surface,
199262306a36Sopenharmony_ci		    uint32_t sid,
199362306a36Sopenharmony_ci		    int32_t destX, int32_t destY,
199462306a36Sopenharmony_ci		    struct drm_vmw_rect *clips,
199562306a36Sopenharmony_ci		    uint32_t num_clips)
199662306a36Sopenharmony_ci{
199762306a36Sopenharmony_ci	int ret;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	switch (dev_priv->active_display_unit) {
200062306a36Sopenharmony_ci	case vmw_du_screen_target:
200162306a36Sopenharmony_ci		ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips,
200262306a36Sopenharmony_ci						 &surface->res, destX, destY,
200362306a36Sopenharmony_ci						 num_clips, 1, NULL, NULL);
200462306a36Sopenharmony_ci		break;
200562306a36Sopenharmony_ci	case vmw_du_screen_object:
200662306a36Sopenharmony_ci		ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface,
200762306a36Sopenharmony_ci					      sid, destX, destY, clips,
200862306a36Sopenharmony_ci					      num_clips);
200962306a36Sopenharmony_ci		break;
201062306a36Sopenharmony_ci	default:
201162306a36Sopenharmony_ci		WARN_ONCE(true,
201262306a36Sopenharmony_ci			  "Present called with invalid display system.\n");
201362306a36Sopenharmony_ci		ret = -ENOSYS;
201462306a36Sopenharmony_ci		break;
201562306a36Sopenharmony_ci	}
201662306a36Sopenharmony_ci	if (ret)
201762306a36Sopenharmony_ci		return ret;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	vmw_cmd_flush(dev_priv, false);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	return 0;
202262306a36Sopenharmony_ci}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_cistatic void
202562306a36Sopenharmony_civmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv)
202662306a36Sopenharmony_ci{
202762306a36Sopenharmony_ci	if (dev_priv->hotplug_mode_update_property)
202862306a36Sopenharmony_ci		return;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	dev_priv->hotplug_mode_update_property =
203162306a36Sopenharmony_ci		drm_property_create_range(&dev_priv->drm,
203262306a36Sopenharmony_ci					  DRM_MODE_PROP_IMMUTABLE,
203362306a36Sopenharmony_ci					  "hotplug_mode_update", 0, 1);
203462306a36Sopenharmony_ci}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ciint vmw_kms_init(struct vmw_private *dev_priv)
203762306a36Sopenharmony_ci{
203862306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
203962306a36Sopenharmony_ci	int ret;
204062306a36Sopenharmony_ci	static const char *display_unit_names[] = {
204162306a36Sopenharmony_ci		"Invalid",
204262306a36Sopenharmony_ci		"Legacy",
204362306a36Sopenharmony_ci		"Screen Object",
204462306a36Sopenharmony_ci		"Screen Target",
204562306a36Sopenharmony_ci		"Invalid (max)"
204662306a36Sopenharmony_ci	};
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	drm_mode_config_init(dev);
204962306a36Sopenharmony_ci	dev->mode_config.funcs = &vmw_kms_funcs;
205062306a36Sopenharmony_ci	dev->mode_config.min_width = 1;
205162306a36Sopenharmony_ci	dev->mode_config.min_height = 1;
205262306a36Sopenharmony_ci	dev->mode_config.max_width = dev_priv->texture_max_width;
205362306a36Sopenharmony_ci	dev->mode_config.max_height = dev_priv->texture_max_height;
205462306a36Sopenharmony_ci	dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	drm_mode_create_suggested_offset_properties(dev);
205762306a36Sopenharmony_ci	vmw_kms_create_hotplug_mode_update_property(dev_priv);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	ret = vmw_kms_stdu_init_display(dev_priv);
206062306a36Sopenharmony_ci	if (ret) {
206162306a36Sopenharmony_ci		ret = vmw_kms_sou_init_display(dev_priv);
206262306a36Sopenharmony_ci		if (ret) /* Fallback */
206362306a36Sopenharmony_ci			ret = vmw_kms_ldu_init_display(dev_priv);
206462306a36Sopenharmony_ci	}
206562306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(display_unit_names) != (vmw_du_max + 1));
206662306a36Sopenharmony_ci	drm_info(&dev_priv->drm, "%s display unit initialized\n",
206762306a36Sopenharmony_ci		 display_unit_names[dev_priv->active_display_unit]);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	return ret;
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ciint vmw_kms_close(struct vmw_private *dev_priv)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	int ret = 0;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	/*
207762306a36Sopenharmony_ci	 * Docs says we should take the lock before calling this function
207862306a36Sopenharmony_ci	 * but since it destroys encoders and our destructor calls
207962306a36Sopenharmony_ci	 * drm_encoder_cleanup which takes the lock we deadlock.
208062306a36Sopenharmony_ci	 */
208162306a36Sopenharmony_ci	drm_mode_config_cleanup(&dev_priv->drm);
208262306a36Sopenharmony_ci	if (dev_priv->active_display_unit == vmw_du_legacy)
208362306a36Sopenharmony_ci		ret = vmw_kms_ldu_close_display(dev_priv);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	return ret;
208662306a36Sopenharmony_ci}
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ciint vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
208962306a36Sopenharmony_ci				struct drm_file *file_priv)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	struct drm_vmw_cursor_bypass_arg *arg = data;
209262306a36Sopenharmony_ci	struct vmw_display_unit *du;
209362306a36Sopenharmony_ci	struct drm_crtc *crtc;
209462306a36Sopenharmony_ci	int ret = 0;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
209762306a36Sopenharmony_ci	if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
210062306a36Sopenharmony_ci			du = vmw_crtc_to_du(crtc);
210162306a36Sopenharmony_ci			du->hotspot_x = arg->xhot;
210262306a36Sopenharmony_ci			du->hotspot_y = arg->yhot;
210362306a36Sopenharmony_ci		}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci		mutex_unlock(&dev->mode_config.mutex);
210662306a36Sopenharmony_ci		return 0;
210762306a36Sopenharmony_ci	}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	crtc = drm_crtc_find(dev, file_priv, arg->crtc_id);
211062306a36Sopenharmony_ci	if (!crtc) {
211162306a36Sopenharmony_ci		ret = -ENOENT;
211262306a36Sopenharmony_ci		goto out;
211362306a36Sopenharmony_ci	}
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	du = vmw_crtc_to_du(crtc);
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	du->hotspot_x = arg->xhot;
211862306a36Sopenharmony_ci	du->hotspot_y = arg->yhot;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ciout:
212162306a36Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	return ret;
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ciint vmw_kms_write_svga(struct vmw_private *vmw_priv,
212762306a36Sopenharmony_ci			unsigned width, unsigned height, unsigned pitch,
212862306a36Sopenharmony_ci			unsigned bpp, unsigned depth)
212962306a36Sopenharmony_ci{
213062306a36Sopenharmony_ci	if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
213162306a36Sopenharmony_ci		vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch);
213262306a36Sopenharmony_ci	else if (vmw_fifo_have_pitchlock(vmw_priv))
213362306a36Sopenharmony_ci		vmw_fifo_mem_write(vmw_priv, SVGA_FIFO_PITCHLOCK, pitch);
213462306a36Sopenharmony_ci	vmw_write(vmw_priv, SVGA_REG_WIDTH, width);
213562306a36Sopenharmony_ci	vmw_write(vmw_priv, SVGA_REG_HEIGHT, height);
213662306a36Sopenharmony_ci	if ((vmw_priv->capabilities & SVGA_CAP_8BIT_EMULATION) != 0)
213762306a36Sopenharmony_ci		vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) {
214062306a36Sopenharmony_ci		DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n",
214162306a36Sopenharmony_ci			  depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH));
214262306a36Sopenharmony_ci		return -EINVAL;
214362306a36Sopenharmony_ci	}
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	return 0;
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cibool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
214962306a36Sopenharmony_ci				uint32_t pitch,
215062306a36Sopenharmony_ci				uint32_t height)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	return ((u64) pitch * (u64) height) < (u64)
215362306a36Sopenharmony_ci		((dev_priv->active_display_unit == vmw_du_screen_target) ?
215462306a36Sopenharmony_ci		 dev_priv->max_primary_mem : dev_priv->vram_size);
215562306a36Sopenharmony_ci}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci/**
215862306a36Sopenharmony_ci * vmw_du_update_layout - Update the display unit with topology from resolution
215962306a36Sopenharmony_ci * plugin and generate DRM uevent
216062306a36Sopenharmony_ci * @dev_priv: device private
216162306a36Sopenharmony_ci * @num_rects: number of drm_rect in rects
216262306a36Sopenharmony_ci * @rects: toplogy to update
216362306a36Sopenharmony_ci */
216462306a36Sopenharmony_cistatic int vmw_du_update_layout(struct vmw_private *dev_priv,
216562306a36Sopenharmony_ci				unsigned int num_rects, struct drm_rect *rects)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	struct drm_device *dev = &dev_priv->drm;
216862306a36Sopenharmony_ci	struct vmw_display_unit *du;
216962306a36Sopenharmony_ci	struct drm_connector *con;
217062306a36Sopenharmony_ci	struct drm_connector_list_iter conn_iter;
217162306a36Sopenharmony_ci	struct drm_modeset_acquire_ctx ctx;
217262306a36Sopenharmony_ci	struct drm_crtc *crtc;
217362306a36Sopenharmony_ci	int ret;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	/* Currently gui_x/y is protected with the crtc mutex */
217662306a36Sopenharmony_ci	mutex_lock(&dev->mode_config.mutex);
217762306a36Sopenharmony_ci	drm_modeset_acquire_init(&ctx, 0);
217862306a36Sopenharmony_ciretry:
217962306a36Sopenharmony_ci	drm_for_each_crtc(crtc, dev) {
218062306a36Sopenharmony_ci		ret = drm_modeset_lock(&crtc->mutex, &ctx);
218162306a36Sopenharmony_ci		if (ret < 0) {
218262306a36Sopenharmony_ci			if (ret == -EDEADLK) {
218362306a36Sopenharmony_ci				drm_modeset_backoff(&ctx);
218462306a36Sopenharmony_ci				goto retry;
218562306a36Sopenharmony_ci		}
218662306a36Sopenharmony_ci			goto out_fini;
218762306a36Sopenharmony_ci		}
218862306a36Sopenharmony_ci	}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	drm_connector_list_iter_begin(dev, &conn_iter);
219162306a36Sopenharmony_ci	drm_for_each_connector_iter(con, &conn_iter) {
219262306a36Sopenharmony_ci		du = vmw_connector_to_du(con);
219362306a36Sopenharmony_ci		if (num_rects > du->unit) {
219462306a36Sopenharmony_ci			du->pref_width = drm_rect_width(&rects[du->unit]);
219562306a36Sopenharmony_ci			du->pref_height = drm_rect_height(&rects[du->unit]);
219662306a36Sopenharmony_ci			du->pref_active = true;
219762306a36Sopenharmony_ci			du->gui_x = rects[du->unit].x1;
219862306a36Sopenharmony_ci			du->gui_y = rects[du->unit].y1;
219962306a36Sopenharmony_ci		} else {
220062306a36Sopenharmony_ci			du->pref_width  = VMWGFX_MIN_INITIAL_WIDTH;
220162306a36Sopenharmony_ci			du->pref_height = VMWGFX_MIN_INITIAL_HEIGHT;
220262306a36Sopenharmony_ci			du->pref_active = false;
220362306a36Sopenharmony_ci			du->gui_x = 0;
220462306a36Sopenharmony_ci			du->gui_y = 0;
220562306a36Sopenharmony_ci		}
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci	drm_connector_list_iter_end(&conn_iter);
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	list_for_each_entry(con, &dev->mode_config.connector_list, head) {
221062306a36Sopenharmony_ci		du = vmw_connector_to_du(con);
221162306a36Sopenharmony_ci		if (num_rects > du->unit) {
221262306a36Sopenharmony_ci			drm_object_property_set_value
221362306a36Sopenharmony_ci			  (&con->base, dev->mode_config.suggested_x_property,
221462306a36Sopenharmony_ci			   du->gui_x);
221562306a36Sopenharmony_ci			drm_object_property_set_value
221662306a36Sopenharmony_ci			  (&con->base, dev->mode_config.suggested_y_property,
221762306a36Sopenharmony_ci			   du->gui_y);
221862306a36Sopenharmony_ci		} else {
221962306a36Sopenharmony_ci			drm_object_property_set_value
222062306a36Sopenharmony_ci			  (&con->base, dev->mode_config.suggested_x_property,
222162306a36Sopenharmony_ci			   0);
222262306a36Sopenharmony_ci			drm_object_property_set_value
222362306a36Sopenharmony_ci			  (&con->base, dev->mode_config.suggested_y_property,
222462306a36Sopenharmony_ci			   0);
222562306a36Sopenharmony_ci		}
222662306a36Sopenharmony_ci		con->status = vmw_du_connector_detect(con, true);
222762306a36Sopenharmony_ci	}
222862306a36Sopenharmony_ciout_fini:
222962306a36Sopenharmony_ci	drm_modeset_drop_locks(&ctx);
223062306a36Sopenharmony_ci	drm_modeset_acquire_fini(&ctx);
223162306a36Sopenharmony_ci	mutex_unlock(&dev->mode_config.mutex);
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	drm_sysfs_hotplug_event(dev);
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	return 0;
223662306a36Sopenharmony_ci}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ciint vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
223962306a36Sopenharmony_ci			  u16 *r, u16 *g, u16 *b,
224062306a36Sopenharmony_ci			  uint32_t size,
224162306a36Sopenharmony_ci			  struct drm_modeset_acquire_ctx *ctx)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
224462306a36Sopenharmony_ci	int i;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
224762306a36Sopenharmony_ci		DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i,
224862306a36Sopenharmony_ci			  r[i], g[i], b[i]);
224962306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8);
225062306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8);
225162306a36Sopenharmony_ci		vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8);
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	return 0;
225562306a36Sopenharmony_ci}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ciint vmw_du_connector_dpms(struct drm_connector *connector, int mode)
225862306a36Sopenharmony_ci{
225962306a36Sopenharmony_ci	return 0;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cienum drm_connector_status
226362306a36Sopenharmony_civmw_du_connector_detect(struct drm_connector *connector, bool force)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	uint32_t num_displays;
226662306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
226762306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
226862306a36Sopenharmony_ci	struct vmw_display_unit *du = vmw_connector_to_du(connector);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	return ((vmw_connector_to_du(connector)->unit < num_displays &&
227362306a36Sopenharmony_ci		 du->pref_active) ?
227462306a36Sopenharmony_ci		connector_status_connected : connector_status_disconnected);
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_cistatic struct drm_display_mode vmw_kms_connector_builtin[] = {
227862306a36Sopenharmony_ci	/* 640x480@60Hz */
227962306a36Sopenharmony_ci	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
228062306a36Sopenharmony_ci		   752, 800, 0, 480, 489, 492, 525, 0,
228162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
228262306a36Sopenharmony_ci	/* 800x600@60Hz */
228362306a36Sopenharmony_ci	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
228462306a36Sopenharmony_ci		   968, 1056, 0, 600, 601, 605, 628, 0,
228562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
228662306a36Sopenharmony_ci	/* 1024x768@60Hz */
228762306a36Sopenharmony_ci	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
228862306a36Sopenharmony_ci		   1184, 1344, 0, 768, 771, 777, 806, 0,
228962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
229062306a36Sopenharmony_ci	/* 1152x864@75Hz */
229162306a36Sopenharmony_ci	{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
229262306a36Sopenharmony_ci		   1344, 1600, 0, 864, 865, 868, 900, 0,
229362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
229462306a36Sopenharmony_ci	/* 1280x720@60Hz */
229562306a36Sopenharmony_ci	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74500, 1280, 1344,
229662306a36Sopenharmony_ci		   1472, 1664, 0, 720, 723, 728, 748, 0,
229762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
229862306a36Sopenharmony_ci	/* 1280x768@60Hz */
229962306a36Sopenharmony_ci	{ DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
230062306a36Sopenharmony_ci		   1472, 1664, 0, 768, 771, 778, 798, 0,
230162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
230262306a36Sopenharmony_ci	/* 1280x800@60Hz */
230362306a36Sopenharmony_ci	{ DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
230462306a36Sopenharmony_ci		   1480, 1680, 0, 800, 803, 809, 831, 0,
230562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
230662306a36Sopenharmony_ci	/* 1280x960@60Hz */
230762306a36Sopenharmony_ci	{ DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
230862306a36Sopenharmony_ci		   1488, 1800, 0, 960, 961, 964, 1000, 0,
230962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
231062306a36Sopenharmony_ci	/* 1280x1024@60Hz */
231162306a36Sopenharmony_ci	{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
231262306a36Sopenharmony_ci		   1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
231362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
231462306a36Sopenharmony_ci	/* 1360x768@60Hz */
231562306a36Sopenharmony_ci	{ DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
231662306a36Sopenharmony_ci		   1536, 1792, 0, 768, 771, 777, 795, 0,
231762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
231862306a36Sopenharmony_ci	/* 1440x1050@60Hz */
231962306a36Sopenharmony_ci	{ DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
232062306a36Sopenharmony_ci		   1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
232162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
232262306a36Sopenharmony_ci	/* 1440x900@60Hz */
232362306a36Sopenharmony_ci	{ DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
232462306a36Sopenharmony_ci		   1672, 1904, 0, 900, 903, 909, 934, 0,
232562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
232662306a36Sopenharmony_ci	/* 1600x1200@60Hz */
232762306a36Sopenharmony_ci	{ DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
232862306a36Sopenharmony_ci		   1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
232962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
233062306a36Sopenharmony_ci	/* 1680x1050@60Hz */
233162306a36Sopenharmony_ci	{ DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
233262306a36Sopenharmony_ci		   1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
233362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
233462306a36Sopenharmony_ci	/* 1792x1344@60Hz */
233562306a36Sopenharmony_ci	{ DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
233662306a36Sopenharmony_ci		   2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
233762306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
233862306a36Sopenharmony_ci	/* 1853x1392@60Hz */
233962306a36Sopenharmony_ci	{ DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
234062306a36Sopenharmony_ci		   2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
234162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
234262306a36Sopenharmony_ci	/* 1920x1080@60Hz */
234362306a36Sopenharmony_ci	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 173000, 1920, 2048,
234462306a36Sopenharmony_ci		   2248, 2576, 0, 1080, 1083, 1088, 1120, 0,
234562306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
234662306a36Sopenharmony_ci	/* 1920x1200@60Hz */
234762306a36Sopenharmony_ci	{ DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
234862306a36Sopenharmony_ci		   2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
234962306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
235062306a36Sopenharmony_ci	/* 1920x1440@60Hz */
235162306a36Sopenharmony_ci	{ DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
235262306a36Sopenharmony_ci		   2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
235362306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
235462306a36Sopenharmony_ci	/* 2560x1440@60Hz */
235562306a36Sopenharmony_ci	{ DRM_MODE("2560x1440", DRM_MODE_TYPE_DRIVER, 241500, 2560, 2608,
235662306a36Sopenharmony_ci		   2640, 2720, 0, 1440, 1443, 1448, 1481, 0,
235762306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
235862306a36Sopenharmony_ci	/* 2560x1600@60Hz */
235962306a36Sopenharmony_ci	{ DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
236062306a36Sopenharmony_ci		   3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
236162306a36Sopenharmony_ci		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
236262306a36Sopenharmony_ci	/* 2880x1800@60Hz */
236362306a36Sopenharmony_ci	{ DRM_MODE("2880x1800", DRM_MODE_TYPE_DRIVER, 337500, 2880, 2928,
236462306a36Sopenharmony_ci		   2960, 3040, 0, 1800, 1803, 1809, 1852, 0,
236562306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
236662306a36Sopenharmony_ci	/* 3840x2160@60Hz */
236762306a36Sopenharmony_ci	{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 533000, 3840, 3888,
236862306a36Sopenharmony_ci		   3920, 4000, 0, 2160, 2163, 2168, 2222, 0,
236962306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
237062306a36Sopenharmony_ci	/* 3840x2400@60Hz */
237162306a36Sopenharmony_ci	{ DRM_MODE("3840x2400", DRM_MODE_TYPE_DRIVER, 592250, 3840, 3888,
237262306a36Sopenharmony_ci		   3920, 4000, 0, 2400, 2403, 2409, 2469, 0,
237362306a36Sopenharmony_ci		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
237462306a36Sopenharmony_ci	/* Terminate */
237562306a36Sopenharmony_ci	{ DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
237662306a36Sopenharmony_ci};
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci/**
237962306a36Sopenharmony_ci * vmw_guess_mode_timing - Provide fake timings for a
238062306a36Sopenharmony_ci * 60Hz vrefresh mode.
238162306a36Sopenharmony_ci *
238262306a36Sopenharmony_ci * @mode: Pointer to a struct drm_display_mode with hdisplay and vdisplay
238362306a36Sopenharmony_ci * members filled in.
238462306a36Sopenharmony_ci */
238562306a36Sopenharmony_civoid vmw_guess_mode_timing(struct drm_display_mode *mode)
238662306a36Sopenharmony_ci{
238762306a36Sopenharmony_ci	mode->hsync_start = mode->hdisplay + 50;
238862306a36Sopenharmony_ci	mode->hsync_end = mode->hsync_start + 50;
238962306a36Sopenharmony_ci	mode->htotal = mode->hsync_end + 50;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	mode->vsync_start = mode->vdisplay + 50;
239262306a36Sopenharmony_ci	mode->vsync_end = mode->vsync_start + 50;
239362306a36Sopenharmony_ci	mode->vtotal = mode->vsync_end + 50;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	mode->clock = (u32)mode->htotal * (u32)mode->vtotal / 100 * 6;
239662306a36Sopenharmony_ci}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ciint vmw_du_connector_fill_modes(struct drm_connector *connector,
240062306a36Sopenharmony_ci				uint32_t max_width, uint32_t max_height)
240162306a36Sopenharmony_ci{
240262306a36Sopenharmony_ci	struct vmw_display_unit *du = vmw_connector_to_du(connector);
240362306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
240462306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
240562306a36Sopenharmony_ci	struct drm_display_mode *mode = NULL;
240662306a36Sopenharmony_ci	struct drm_display_mode *bmode;
240762306a36Sopenharmony_ci	struct drm_display_mode prefmode = { DRM_MODE("preferred",
240862306a36Sopenharmony_ci		DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
240962306a36Sopenharmony_ci		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
241062306a36Sopenharmony_ci		DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
241162306a36Sopenharmony_ci	};
241262306a36Sopenharmony_ci	int i;
241362306a36Sopenharmony_ci	u32 assumed_bpp = 4;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	if (dev_priv->assume_16bpp)
241662306a36Sopenharmony_ci		assumed_bpp = 2;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	max_width  = min(max_width,  dev_priv->texture_max_width);
241962306a36Sopenharmony_ci	max_height = min(max_height, dev_priv->texture_max_height);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	/*
242262306a36Sopenharmony_ci	 * For STDU extra limit for a mode on SVGA_REG_SCREENTARGET_MAX_WIDTH/
242362306a36Sopenharmony_ci	 * HEIGHT registers.
242462306a36Sopenharmony_ci	 */
242562306a36Sopenharmony_ci	if (dev_priv->active_display_unit == vmw_du_screen_target) {
242662306a36Sopenharmony_ci		max_width  = min(max_width,  dev_priv->stdu_max_width);
242762306a36Sopenharmony_ci		max_height = min(max_height, dev_priv->stdu_max_height);
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	/* Add preferred mode */
243162306a36Sopenharmony_ci	mode = drm_mode_duplicate(dev, &prefmode);
243262306a36Sopenharmony_ci	if (!mode)
243362306a36Sopenharmony_ci		return 0;
243462306a36Sopenharmony_ci	mode->hdisplay = du->pref_width;
243562306a36Sopenharmony_ci	mode->vdisplay = du->pref_height;
243662306a36Sopenharmony_ci	vmw_guess_mode_timing(mode);
243762306a36Sopenharmony_ci	drm_mode_set_name(mode);
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	if (vmw_kms_validate_mode_vram(dev_priv,
244062306a36Sopenharmony_ci					mode->hdisplay * assumed_bpp,
244162306a36Sopenharmony_ci					mode->vdisplay)) {
244262306a36Sopenharmony_ci		drm_mode_probed_add(connector, mode);
244362306a36Sopenharmony_ci	} else {
244462306a36Sopenharmony_ci		drm_mode_destroy(dev, mode);
244562306a36Sopenharmony_ci		mode = NULL;
244662306a36Sopenharmony_ci	}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	if (du->pref_mode) {
244962306a36Sopenharmony_ci		list_del_init(&du->pref_mode->head);
245062306a36Sopenharmony_ci		drm_mode_destroy(dev, du->pref_mode);
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	/* mode might be null here, this is intended */
245462306a36Sopenharmony_ci	du->pref_mode = mode;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) {
245762306a36Sopenharmony_ci		bmode = &vmw_kms_connector_builtin[i];
245862306a36Sopenharmony_ci		if (bmode->hdisplay > max_width ||
245962306a36Sopenharmony_ci		    bmode->vdisplay > max_height)
246062306a36Sopenharmony_ci			continue;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci		if (!vmw_kms_validate_mode_vram(dev_priv,
246362306a36Sopenharmony_ci						bmode->hdisplay * assumed_bpp,
246462306a36Sopenharmony_ci						bmode->vdisplay))
246562306a36Sopenharmony_ci			continue;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		mode = drm_mode_duplicate(dev, bmode);
246862306a36Sopenharmony_ci		if (!mode)
246962306a36Sopenharmony_ci			return 0;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci		drm_mode_probed_add(connector, mode);
247262306a36Sopenharmony_ci	}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	drm_connector_list_update(connector);
247562306a36Sopenharmony_ci	/* Move the prefered mode first, help apps pick the right mode. */
247662306a36Sopenharmony_ci	drm_mode_sort(&connector->modes);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	return 1;
247962306a36Sopenharmony_ci}
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci/**
248262306a36Sopenharmony_ci * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl
248362306a36Sopenharmony_ci * @dev: drm device for the ioctl
248462306a36Sopenharmony_ci * @data: data pointer for the ioctl
248562306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call
248662306a36Sopenharmony_ci *
248762306a36Sopenharmony_ci * Update preferred topology of display unit as per ioctl request. The topology
248862306a36Sopenharmony_ci * is expressed as array of drm_vmw_rect.
248962306a36Sopenharmony_ci * e.g.
249062306a36Sopenharmony_ci * [0 0 640 480] [640 0 800 600] [0 480 640 480]
249162306a36Sopenharmony_ci *
249262306a36Sopenharmony_ci * NOTE:
249362306a36Sopenharmony_ci * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside
249462306a36Sopenharmony_ci * device limit on topology, x + w and y + h (lower right) cannot be greater
249562306a36Sopenharmony_ci * than INT_MAX. So topology beyond these limits will return with error.
249662306a36Sopenharmony_ci *
249762306a36Sopenharmony_ci * Returns:
249862306a36Sopenharmony_ci * Zero on success, negative errno on failure.
249962306a36Sopenharmony_ci */
250062306a36Sopenharmony_ciint vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
250162306a36Sopenharmony_ci				struct drm_file *file_priv)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
250462306a36Sopenharmony_ci	struct drm_mode_config *mode_config = &dev->mode_config;
250562306a36Sopenharmony_ci	struct drm_vmw_update_layout_arg *arg =
250662306a36Sopenharmony_ci		(struct drm_vmw_update_layout_arg *)data;
250762306a36Sopenharmony_ci	void __user *user_rects;
250862306a36Sopenharmony_ci	struct drm_vmw_rect *rects;
250962306a36Sopenharmony_ci	struct drm_rect *drm_rects;
251062306a36Sopenharmony_ci	unsigned rects_size;
251162306a36Sopenharmony_ci	int ret, i;
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	if (!arg->num_outputs) {
251462306a36Sopenharmony_ci		struct drm_rect def_rect = {0, 0,
251562306a36Sopenharmony_ci					    VMWGFX_MIN_INITIAL_WIDTH,
251662306a36Sopenharmony_ci					    VMWGFX_MIN_INITIAL_HEIGHT};
251762306a36Sopenharmony_ci		vmw_du_update_layout(dev_priv, 1, &def_rect);
251862306a36Sopenharmony_ci		return 0;
251962306a36Sopenharmony_ci	}
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect);
252262306a36Sopenharmony_ci	rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect),
252362306a36Sopenharmony_ci			GFP_KERNEL);
252462306a36Sopenharmony_ci	if (unlikely(!rects))
252562306a36Sopenharmony_ci		return -ENOMEM;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	user_rects = (void __user *)(unsigned long)arg->rects;
252862306a36Sopenharmony_ci	ret = copy_from_user(rects, user_rects, rects_size);
252962306a36Sopenharmony_ci	if (unlikely(ret != 0)) {
253062306a36Sopenharmony_ci		DRM_ERROR("Failed to get rects.\n");
253162306a36Sopenharmony_ci		ret = -EFAULT;
253262306a36Sopenharmony_ci		goto out_free;
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	drm_rects = (struct drm_rect *)rects;
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	VMW_DEBUG_KMS("Layout count = %u\n", arg->num_outputs);
253862306a36Sopenharmony_ci	for (i = 0; i < arg->num_outputs; i++) {
253962306a36Sopenharmony_ci		struct drm_vmw_rect curr_rect;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci		/* Verify user-space for overflow as kernel use drm_rect */
254262306a36Sopenharmony_ci		if ((rects[i].x + rects[i].w > INT_MAX) ||
254362306a36Sopenharmony_ci		    (rects[i].y + rects[i].h > INT_MAX)) {
254462306a36Sopenharmony_ci			ret = -ERANGE;
254562306a36Sopenharmony_ci			goto out_free;
254662306a36Sopenharmony_ci		}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci		curr_rect = rects[i];
254962306a36Sopenharmony_ci		drm_rects[i].x1 = curr_rect.x;
255062306a36Sopenharmony_ci		drm_rects[i].y1 = curr_rect.y;
255162306a36Sopenharmony_ci		drm_rects[i].x2 = curr_rect.x + curr_rect.w;
255262306a36Sopenharmony_ci		drm_rects[i].y2 = curr_rect.y + curr_rect.h;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci		VMW_DEBUG_KMS("  x1 = %d y1 = %d x2 = %d y2 = %d\n",
255562306a36Sopenharmony_ci			      drm_rects[i].x1, drm_rects[i].y1,
255662306a36Sopenharmony_ci			      drm_rects[i].x2, drm_rects[i].y2);
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci		/*
255962306a36Sopenharmony_ci		 * Currently this check is limiting the topology within
256062306a36Sopenharmony_ci		 * mode_config->max (which actually is max texture size
256162306a36Sopenharmony_ci		 * supported by virtual device). This limit is here to address
256262306a36Sopenharmony_ci		 * window managers that create a big framebuffer for whole
256362306a36Sopenharmony_ci		 * topology.
256462306a36Sopenharmony_ci		 */
256562306a36Sopenharmony_ci		if (drm_rects[i].x1 < 0 ||  drm_rects[i].y1 < 0 ||
256662306a36Sopenharmony_ci		    drm_rects[i].x2 > mode_config->max_width ||
256762306a36Sopenharmony_ci		    drm_rects[i].y2 > mode_config->max_height) {
256862306a36Sopenharmony_ci			VMW_DEBUG_KMS("Invalid layout %d %d %d %d\n",
256962306a36Sopenharmony_ci				      drm_rects[i].x1, drm_rects[i].y1,
257062306a36Sopenharmony_ci				      drm_rects[i].x2, drm_rects[i].y2);
257162306a36Sopenharmony_ci			ret = -EINVAL;
257262306a36Sopenharmony_ci			goto out_free;
257362306a36Sopenharmony_ci		}
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects);
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	if (ret == 0)
257962306a36Sopenharmony_ci		vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects);
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ciout_free:
258262306a36Sopenharmony_ci	kfree(rects);
258362306a36Sopenharmony_ci	return ret;
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci/**
258762306a36Sopenharmony_ci * vmw_kms_helper_dirty - Helper to build commands and perform actions based
258862306a36Sopenharmony_ci * on a set of cliprects and a set of display units.
258962306a36Sopenharmony_ci *
259062306a36Sopenharmony_ci * @dev_priv: Pointer to a device private structure.
259162306a36Sopenharmony_ci * @framebuffer: Pointer to the framebuffer on which to perform the actions.
259262306a36Sopenharmony_ci * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL.
259362306a36Sopenharmony_ci * Cliprects are given in framebuffer coordinates.
259462306a36Sopenharmony_ci * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must
259562306a36Sopenharmony_ci * be NULL. Cliprects are given in source coordinates.
259662306a36Sopenharmony_ci * @dest_x: X coordinate offset for the crtc / destination clip rects.
259762306a36Sopenharmony_ci * @dest_y: Y coordinate offset for the crtc / destination clip rects.
259862306a36Sopenharmony_ci * @num_clips: Number of cliprects in the @clips or @vclips array.
259962306a36Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping.
260062306a36Sopenharmony_ci * Used to skip a predetermined number of clip rects.
260162306a36Sopenharmony_ci * @dirty: Closure structure. See the description of struct vmw_kms_dirty.
260262306a36Sopenharmony_ci */
260362306a36Sopenharmony_ciint vmw_kms_helper_dirty(struct vmw_private *dev_priv,
260462306a36Sopenharmony_ci			 struct vmw_framebuffer *framebuffer,
260562306a36Sopenharmony_ci			 const struct drm_clip_rect *clips,
260662306a36Sopenharmony_ci			 const struct drm_vmw_rect *vclips,
260762306a36Sopenharmony_ci			 s32 dest_x, s32 dest_y,
260862306a36Sopenharmony_ci			 int num_clips,
260962306a36Sopenharmony_ci			 int increment,
261062306a36Sopenharmony_ci			 struct vmw_kms_dirty *dirty)
261162306a36Sopenharmony_ci{
261262306a36Sopenharmony_ci	struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
261362306a36Sopenharmony_ci	struct drm_crtc *crtc;
261462306a36Sopenharmony_ci	u32 num_units = 0;
261562306a36Sopenharmony_ci	u32 i, k;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	dirty->dev_priv = dev_priv;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	/* If crtc is passed, no need to iterate over other display units */
262062306a36Sopenharmony_ci	if (dirty->crtc) {
262162306a36Sopenharmony_ci		units[num_units++] = vmw_crtc_to_du(dirty->crtc);
262262306a36Sopenharmony_ci	} else {
262362306a36Sopenharmony_ci		list_for_each_entry(crtc, &dev_priv->drm.mode_config.crtc_list,
262462306a36Sopenharmony_ci				    head) {
262562306a36Sopenharmony_ci			struct drm_plane *plane = crtc->primary;
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci			if (plane->state->fb == &framebuffer->base)
262862306a36Sopenharmony_ci				units[num_units++] = vmw_crtc_to_du(crtc);
262962306a36Sopenharmony_ci		}
263062306a36Sopenharmony_ci	}
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	for (k = 0; k < num_units; k++) {
263362306a36Sopenharmony_ci		struct vmw_display_unit *unit = units[k];
263462306a36Sopenharmony_ci		s32 crtc_x = unit->crtc.x;
263562306a36Sopenharmony_ci		s32 crtc_y = unit->crtc.y;
263662306a36Sopenharmony_ci		s32 crtc_width = unit->crtc.mode.hdisplay;
263762306a36Sopenharmony_ci		s32 crtc_height = unit->crtc.mode.vdisplay;
263862306a36Sopenharmony_ci		const struct drm_clip_rect *clips_ptr = clips;
263962306a36Sopenharmony_ci		const struct drm_vmw_rect *vclips_ptr = vclips;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci		dirty->unit = unit;
264262306a36Sopenharmony_ci		if (dirty->fifo_reserve_size > 0) {
264362306a36Sopenharmony_ci			dirty->cmd = VMW_CMD_RESERVE(dev_priv,
264462306a36Sopenharmony_ci						      dirty->fifo_reserve_size);
264562306a36Sopenharmony_ci			if (!dirty->cmd)
264662306a36Sopenharmony_ci				return -ENOMEM;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci			memset(dirty->cmd, 0, dirty->fifo_reserve_size);
264962306a36Sopenharmony_ci		}
265062306a36Sopenharmony_ci		dirty->num_hits = 0;
265162306a36Sopenharmony_ci		for (i = 0; i < num_clips; i++, clips_ptr += increment,
265262306a36Sopenharmony_ci		       vclips_ptr += increment) {
265362306a36Sopenharmony_ci			s32 clip_left;
265462306a36Sopenharmony_ci			s32 clip_top;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci			/*
265762306a36Sopenharmony_ci			 * Select clip array type. Note that integer type
265862306a36Sopenharmony_ci			 * in @clips is unsigned short, whereas in @vclips
265962306a36Sopenharmony_ci			 * it's 32-bit.
266062306a36Sopenharmony_ci			 */
266162306a36Sopenharmony_ci			if (clips) {
266262306a36Sopenharmony_ci				dirty->fb_x = (s32) clips_ptr->x1;
266362306a36Sopenharmony_ci				dirty->fb_y = (s32) clips_ptr->y1;
266462306a36Sopenharmony_ci				dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x -
266562306a36Sopenharmony_ci					crtc_x;
266662306a36Sopenharmony_ci				dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y -
266762306a36Sopenharmony_ci					crtc_y;
266862306a36Sopenharmony_ci			} else {
266962306a36Sopenharmony_ci				dirty->fb_x = vclips_ptr->x;
267062306a36Sopenharmony_ci				dirty->fb_y = vclips_ptr->y;
267162306a36Sopenharmony_ci				dirty->unit_x2 = dirty->fb_x + vclips_ptr->w +
267262306a36Sopenharmony_ci					dest_x - crtc_x;
267362306a36Sopenharmony_ci				dirty->unit_y2 = dirty->fb_y + vclips_ptr->h +
267462306a36Sopenharmony_ci					dest_y - crtc_y;
267562306a36Sopenharmony_ci			}
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci			dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x;
267862306a36Sopenharmony_ci			dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y;
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci			/* Skip this clip if it's outside the crtc region */
268162306a36Sopenharmony_ci			if (dirty->unit_x1 >= crtc_width ||
268262306a36Sopenharmony_ci			    dirty->unit_y1 >= crtc_height ||
268362306a36Sopenharmony_ci			    dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0)
268462306a36Sopenharmony_ci				continue;
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci			/* Clip right and bottom to crtc limits */
268762306a36Sopenharmony_ci			dirty->unit_x2 = min_t(s32, dirty->unit_x2,
268862306a36Sopenharmony_ci					       crtc_width);
268962306a36Sopenharmony_ci			dirty->unit_y2 = min_t(s32, dirty->unit_y2,
269062306a36Sopenharmony_ci					       crtc_height);
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci			/* Clip left and top to crtc limits */
269362306a36Sopenharmony_ci			clip_left = min_t(s32, dirty->unit_x1, 0);
269462306a36Sopenharmony_ci			clip_top = min_t(s32, dirty->unit_y1, 0);
269562306a36Sopenharmony_ci			dirty->unit_x1 -= clip_left;
269662306a36Sopenharmony_ci			dirty->unit_y1 -= clip_top;
269762306a36Sopenharmony_ci			dirty->fb_x -= clip_left;
269862306a36Sopenharmony_ci			dirty->fb_y -= clip_top;
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci			dirty->clip(dirty);
270162306a36Sopenharmony_ci		}
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci		dirty->fifo_commit(dirty);
270462306a36Sopenharmony_ci	}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	return 0;
270762306a36Sopenharmony_ci}
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci/**
271062306a36Sopenharmony_ci * vmw_kms_helper_validation_finish - Helper for post KMS command submission
271162306a36Sopenharmony_ci * cleanup and fencing
271262306a36Sopenharmony_ci * @dev_priv: Pointer to the device-private struct
271362306a36Sopenharmony_ci * @file_priv: Pointer identifying the client when user-space fencing is used
271462306a36Sopenharmony_ci * @ctx: Pointer to the validation context
271562306a36Sopenharmony_ci * @out_fence: If non-NULL, returned refcounted fence-pointer
271662306a36Sopenharmony_ci * @user_fence_rep: If non-NULL, pointer to user-space address area
271762306a36Sopenharmony_ci * in which to copy user-space fence info
271862306a36Sopenharmony_ci */
271962306a36Sopenharmony_civoid vmw_kms_helper_validation_finish(struct vmw_private *dev_priv,
272062306a36Sopenharmony_ci				      struct drm_file *file_priv,
272162306a36Sopenharmony_ci				      struct vmw_validation_context *ctx,
272262306a36Sopenharmony_ci				      struct vmw_fence_obj **out_fence,
272362306a36Sopenharmony_ci				      struct drm_vmw_fence_rep __user *
272462306a36Sopenharmony_ci				      user_fence_rep)
272562306a36Sopenharmony_ci{
272662306a36Sopenharmony_ci	struct vmw_fence_obj *fence = NULL;
272762306a36Sopenharmony_ci	uint32_t handle = 0;
272862306a36Sopenharmony_ci	int ret = 0;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	if (file_priv || user_fence_rep || vmw_validation_has_bos(ctx) ||
273162306a36Sopenharmony_ci	    out_fence)
273262306a36Sopenharmony_ci		ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence,
273362306a36Sopenharmony_ci						 file_priv ? &handle : NULL);
273462306a36Sopenharmony_ci	vmw_validation_done(ctx, fence);
273562306a36Sopenharmony_ci	if (file_priv)
273662306a36Sopenharmony_ci		vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
273762306a36Sopenharmony_ci					    ret, user_fence_rep, fence,
273862306a36Sopenharmony_ci					    handle, -1);
273962306a36Sopenharmony_ci	if (out_fence)
274062306a36Sopenharmony_ci		*out_fence = fence;
274162306a36Sopenharmony_ci	else
274262306a36Sopenharmony_ci		vmw_fence_obj_unreference(&fence);
274362306a36Sopenharmony_ci}
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci/**
274662306a36Sopenharmony_ci * vmw_kms_update_proxy - Helper function to update a proxy surface from
274762306a36Sopenharmony_ci * its backing MOB.
274862306a36Sopenharmony_ci *
274962306a36Sopenharmony_ci * @res: Pointer to the surface resource
275062306a36Sopenharmony_ci * @clips: Clip rects in framebuffer (surface) space.
275162306a36Sopenharmony_ci * @num_clips: Number of clips in @clips.
275262306a36Sopenharmony_ci * @increment: Integer with which to increment the clip counter when looping.
275362306a36Sopenharmony_ci * Used to skip a predetermined number of clip rects.
275462306a36Sopenharmony_ci *
275562306a36Sopenharmony_ci * This function makes sure the proxy surface is updated from its backing MOB
275662306a36Sopenharmony_ci * using the region given by @clips. The surface resource @res and its backing
275762306a36Sopenharmony_ci * MOB needs to be reserved and validated on call.
275862306a36Sopenharmony_ci */
275962306a36Sopenharmony_ciint vmw_kms_update_proxy(struct vmw_resource *res,
276062306a36Sopenharmony_ci			 const struct drm_clip_rect *clips,
276162306a36Sopenharmony_ci			 unsigned num_clips,
276262306a36Sopenharmony_ci			 int increment)
276362306a36Sopenharmony_ci{
276462306a36Sopenharmony_ci	struct vmw_private *dev_priv = res->dev_priv;
276562306a36Sopenharmony_ci	struct drm_vmw_size *size = &vmw_res_to_srf(res)->metadata.base_size;
276662306a36Sopenharmony_ci	struct {
276762306a36Sopenharmony_ci		SVGA3dCmdHeader header;
276862306a36Sopenharmony_ci		SVGA3dCmdUpdateGBImage body;
276962306a36Sopenharmony_ci	} *cmd;
277062306a36Sopenharmony_ci	SVGA3dBox *box;
277162306a36Sopenharmony_ci	size_t copy_size = 0;
277262306a36Sopenharmony_ci	int i;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	if (!clips)
277562306a36Sopenharmony_ci		return 0;
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd) * num_clips);
277862306a36Sopenharmony_ci	if (!cmd)
277962306a36Sopenharmony_ci		return -ENOMEM;
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) {
278262306a36Sopenharmony_ci		box = &cmd->body.box;
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci		cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE;
278562306a36Sopenharmony_ci		cmd->header.size = sizeof(cmd->body);
278662306a36Sopenharmony_ci		cmd->body.image.sid = res->id;
278762306a36Sopenharmony_ci		cmd->body.image.face = 0;
278862306a36Sopenharmony_ci		cmd->body.image.mipmap = 0;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci		if (clips->x1 > size->width || clips->x2 > size->width ||
279162306a36Sopenharmony_ci		    clips->y1 > size->height || clips->y2 > size->height) {
279262306a36Sopenharmony_ci			DRM_ERROR("Invalid clips outsize of framebuffer.\n");
279362306a36Sopenharmony_ci			return -EINVAL;
279462306a36Sopenharmony_ci		}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci		box->x = clips->x1;
279762306a36Sopenharmony_ci		box->y = clips->y1;
279862306a36Sopenharmony_ci		box->z = 0;
279962306a36Sopenharmony_ci		box->w = clips->x2 - clips->x1;
280062306a36Sopenharmony_ci		box->h = clips->y2 - clips->y1;
280162306a36Sopenharmony_ci		box->d = 1;
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci		copy_size += sizeof(*cmd);
280462306a36Sopenharmony_ci	}
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	vmw_cmd_commit(dev_priv, copy_size);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	return 0;
280962306a36Sopenharmony_ci}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci/**
281262306a36Sopenharmony_ci * vmw_kms_create_implicit_placement_property - Set up the implicit placement
281362306a36Sopenharmony_ci * property.
281462306a36Sopenharmony_ci *
281562306a36Sopenharmony_ci * @dev_priv: Pointer to a device private struct.
281662306a36Sopenharmony_ci *
281762306a36Sopenharmony_ci * Sets up the implicit placement property unless it's already set up.
281862306a36Sopenharmony_ci */
281962306a36Sopenharmony_civoid
282062306a36Sopenharmony_civmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv)
282162306a36Sopenharmony_ci{
282262306a36Sopenharmony_ci	if (dev_priv->implicit_placement_property)
282362306a36Sopenharmony_ci		return;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	dev_priv->implicit_placement_property =
282662306a36Sopenharmony_ci		drm_property_create_range(&dev_priv->drm,
282762306a36Sopenharmony_ci					  DRM_MODE_PROP_IMMUTABLE,
282862306a36Sopenharmony_ci					  "implicit_placement", 0, 1);
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci/**
283262306a36Sopenharmony_ci * vmw_kms_suspend - Save modesetting state and turn modesetting off.
283362306a36Sopenharmony_ci *
283462306a36Sopenharmony_ci * @dev: Pointer to the drm device
283562306a36Sopenharmony_ci * Return: 0 on success. Negative error code on failure.
283662306a36Sopenharmony_ci */
283762306a36Sopenharmony_ciint vmw_kms_suspend(struct drm_device *dev)
283862306a36Sopenharmony_ci{
283962306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	dev_priv->suspend_state = drm_atomic_helper_suspend(dev);
284262306a36Sopenharmony_ci	if (IS_ERR(dev_priv->suspend_state)) {
284362306a36Sopenharmony_ci		int ret = PTR_ERR(dev_priv->suspend_state);
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci		DRM_ERROR("Failed kms suspend: %d\n", ret);
284662306a36Sopenharmony_ci		dev_priv->suspend_state = NULL;
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci		return ret;
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	return 0;
285262306a36Sopenharmony_ci}
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci/**
285662306a36Sopenharmony_ci * vmw_kms_resume - Re-enable modesetting and restore state
285762306a36Sopenharmony_ci *
285862306a36Sopenharmony_ci * @dev: Pointer to the drm device
285962306a36Sopenharmony_ci * Return: 0 on success. Negative error code on failure.
286062306a36Sopenharmony_ci *
286162306a36Sopenharmony_ci * State is resumed from a previous vmw_kms_suspend(). It's illegal
286262306a36Sopenharmony_ci * to call this function without a previous vmw_kms_suspend().
286362306a36Sopenharmony_ci */
286462306a36Sopenharmony_ciint vmw_kms_resume(struct drm_device *dev)
286562306a36Sopenharmony_ci{
286662306a36Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
286762306a36Sopenharmony_ci	int ret;
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	if (WARN_ON(!dev_priv->suspend_state))
287062306a36Sopenharmony_ci		return 0;
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	ret = drm_atomic_helper_resume(dev, dev_priv->suspend_state);
287362306a36Sopenharmony_ci	dev_priv->suspend_state = NULL;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	return ret;
287662306a36Sopenharmony_ci}
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci/**
287962306a36Sopenharmony_ci * vmw_kms_lost_device - Notify kms that modesetting capabilities will be lost
288062306a36Sopenharmony_ci *
288162306a36Sopenharmony_ci * @dev: Pointer to the drm device
288262306a36Sopenharmony_ci */
288362306a36Sopenharmony_civoid vmw_kms_lost_device(struct drm_device *dev)
288462306a36Sopenharmony_ci{
288562306a36Sopenharmony_ci	drm_atomic_helper_shutdown(dev);
288662306a36Sopenharmony_ci}
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci/**
288962306a36Sopenharmony_ci * vmw_du_helper_plane_update - Helper to do plane update on a display unit.
289062306a36Sopenharmony_ci * @update: The closure structure.
289162306a36Sopenharmony_ci *
289262306a36Sopenharmony_ci * Call this helper after setting callbacks in &vmw_du_update_plane to do plane
289362306a36Sopenharmony_ci * update on display unit.
289462306a36Sopenharmony_ci *
289562306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure.
289662306a36Sopenharmony_ci */
289762306a36Sopenharmony_ciint vmw_du_helper_plane_update(struct vmw_du_update_plane *update)
289862306a36Sopenharmony_ci{
289962306a36Sopenharmony_ci	struct drm_plane_state *state = update->plane->state;
290062306a36Sopenharmony_ci	struct drm_plane_state *old_state = update->old_state;
290162306a36Sopenharmony_ci	struct drm_atomic_helper_damage_iter iter;
290262306a36Sopenharmony_ci	struct drm_rect clip;
290362306a36Sopenharmony_ci	struct drm_rect bb;
290462306a36Sopenharmony_ci	DECLARE_VAL_CONTEXT(val_ctx, NULL, 0);
290562306a36Sopenharmony_ci	uint32_t reserved_size = 0;
290662306a36Sopenharmony_ci	uint32_t submit_size = 0;
290762306a36Sopenharmony_ci	uint32_t curr_size = 0;
290862306a36Sopenharmony_ci	uint32_t num_hits = 0;
290962306a36Sopenharmony_ci	void *cmd_start;
291062306a36Sopenharmony_ci	char *cmd_next;
291162306a36Sopenharmony_ci	int ret;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	/*
291462306a36Sopenharmony_ci	 * Iterate in advance to check if really need plane update and find the
291562306a36Sopenharmony_ci	 * number of clips that actually are in plane src for fifo allocation.
291662306a36Sopenharmony_ci	 */
291762306a36Sopenharmony_ci	drm_atomic_helper_damage_iter_init(&iter, old_state, state);
291862306a36Sopenharmony_ci	drm_atomic_for_each_plane_damage(&iter, &clip)
291962306a36Sopenharmony_ci		num_hits++;
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	if (num_hits == 0)
292262306a36Sopenharmony_ci		return 0;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	if (update->vfb->bo) {
292562306a36Sopenharmony_ci		struct vmw_framebuffer_bo *vfbbo =
292662306a36Sopenharmony_ci			container_of(update->vfb, typeof(*vfbbo), base);
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci		/*
292962306a36Sopenharmony_ci		 * For screen targets we want a mappable bo, for everything else we want
293062306a36Sopenharmony_ci		 * accelerated i.e. host backed (vram or gmr) bo. If the display unit
293162306a36Sopenharmony_ci		 * is not screen target then mob's shouldn't be available.
293262306a36Sopenharmony_ci		 */
293362306a36Sopenharmony_ci		if (update->dev_priv->active_display_unit == vmw_du_screen_target) {
293462306a36Sopenharmony_ci			vmw_bo_placement_set(vfbbo->buffer,
293562306a36Sopenharmony_ci					     VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR,
293662306a36Sopenharmony_ci					     VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR);
293762306a36Sopenharmony_ci		} else {
293862306a36Sopenharmony_ci			WARN_ON(update->dev_priv->has_mob);
293962306a36Sopenharmony_ci			vmw_bo_placement_set_default_accelerated(vfbbo->buffer);
294062306a36Sopenharmony_ci		}
294162306a36Sopenharmony_ci		ret = vmw_validation_add_bo(&val_ctx, vfbbo->buffer);
294262306a36Sopenharmony_ci	} else {
294362306a36Sopenharmony_ci		struct vmw_framebuffer_surface *vfbs =
294462306a36Sopenharmony_ci			container_of(update->vfb, typeof(*vfbs), base);
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci		ret = vmw_validation_add_resource(&val_ctx, &vfbs->surface->res,
294762306a36Sopenharmony_ci						  0, VMW_RES_DIRTY_NONE, NULL,
294862306a36Sopenharmony_ci						  NULL);
294962306a36Sopenharmony_ci	}
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	if (ret)
295262306a36Sopenharmony_ci		return ret;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	ret = vmw_validation_prepare(&val_ctx, update->mutex, update->intr);
295562306a36Sopenharmony_ci	if (ret)
295662306a36Sopenharmony_ci		goto out_unref;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	reserved_size = update->calc_fifo_size(update, num_hits);
295962306a36Sopenharmony_ci	cmd_start = VMW_CMD_RESERVE(update->dev_priv, reserved_size);
296062306a36Sopenharmony_ci	if (!cmd_start) {
296162306a36Sopenharmony_ci		ret = -ENOMEM;
296262306a36Sopenharmony_ci		goto out_revert;
296362306a36Sopenharmony_ci	}
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	cmd_next = cmd_start;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (update->post_prepare) {
296862306a36Sopenharmony_ci		curr_size = update->post_prepare(update, cmd_next);
296962306a36Sopenharmony_ci		cmd_next += curr_size;
297062306a36Sopenharmony_ci		submit_size += curr_size;
297162306a36Sopenharmony_ci	}
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci	if (update->pre_clip) {
297462306a36Sopenharmony_ci		curr_size = update->pre_clip(update, cmd_next, num_hits);
297562306a36Sopenharmony_ci		cmd_next += curr_size;
297662306a36Sopenharmony_ci		submit_size += curr_size;
297762306a36Sopenharmony_ci	}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	bb.x1 = INT_MAX;
298062306a36Sopenharmony_ci	bb.y1 = INT_MAX;
298162306a36Sopenharmony_ci	bb.x2 = INT_MIN;
298262306a36Sopenharmony_ci	bb.y2 = INT_MIN;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	drm_atomic_helper_damage_iter_init(&iter, old_state, state);
298562306a36Sopenharmony_ci	drm_atomic_for_each_plane_damage(&iter, &clip) {
298662306a36Sopenharmony_ci		uint32_t fb_x = clip.x1;
298762306a36Sopenharmony_ci		uint32_t fb_y = clip.y1;
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci		vmw_du_translate_to_crtc(state, &clip);
299062306a36Sopenharmony_ci		if (update->clip) {
299162306a36Sopenharmony_ci			curr_size = update->clip(update, cmd_next, &clip, fb_x,
299262306a36Sopenharmony_ci						 fb_y);
299362306a36Sopenharmony_ci			cmd_next += curr_size;
299462306a36Sopenharmony_ci			submit_size += curr_size;
299562306a36Sopenharmony_ci		}
299662306a36Sopenharmony_ci		bb.x1 = min_t(int, bb.x1, clip.x1);
299762306a36Sopenharmony_ci		bb.y1 = min_t(int, bb.y1, clip.y1);
299862306a36Sopenharmony_ci		bb.x2 = max_t(int, bb.x2, clip.x2);
299962306a36Sopenharmony_ci		bb.y2 = max_t(int, bb.y2, clip.y2);
300062306a36Sopenharmony_ci	}
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	curr_size = update->post_clip(update, cmd_next, &bb);
300362306a36Sopenharmony_ci	submit_size += curr_size;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	if (reserved_size < submit_size)
300662306a36Sopenharmony_ci		submit_size = 0;
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	vmw_cmd_commit(update->dev_priv, submit_size);
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	vmw_kms_helper_validation_finish(update->dev_priv, NULL, &val_ctx,
301162306a36Sopenharmony_ci					 update->out_fence, NULL);
301262306a36Sopenharmony_ci	return ret;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ciout_revert:
301562306a36Sopenharmony_ci	vmw_validation_revert(&val_ctx);
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ciout_unref:
301862306a36Sopenharmony_ci	vmw_validation_unref_lists(&val_ctx);
301962306a36Sopenharmony_ci	return ret;
302062306a36Sopenharmony_ci}
3021