18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci/**************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
128c2ecf20Sopenharmony_ci * the following conditions:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
168c2ecf20Sopenharmony_ci * of the Software.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci **************************************************************************/
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "vmwgfx_drv.h"
298c2ecf20Sopenharmony_ci#include <drm/vmwgfx_drm.h>
308c2ecf20Sopenharmony_ci#include "vmwgfx_kms.h"
318c2ecf20Sopenharmony_ci#include "device_include/svga3d_caps.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistruct svga_3d_compat_cap {
348c2ecf20Sopenharmony_ci	SVGA3dCapsRecordHeader header;
358c2ecf20Sopenharmony_ci	SVGA3dCapPair pairs[SVGA3D_DEVCAP_MAX];
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciint vmw_getparam_ioctl(struct drm_device *dev, void *data,
398c2ecf20Sopenharmony_ci		       struct drm_file *file_priv)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
428c2ecf20Sopenharmony_ci	struct drm_vmw_getparam_arg *param =
438c2ecf20Sopenharmony_ci	    (struct drm_vmw_getparam_arg *)data;
448c2ecf20Sopenharmony_ci	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	switch (param->param) {
478c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_NUM_STREAMS:
488c2ecf20Sopenharmony_ci		param->value = vmw_overlay_num_overlays(dev_priv);
498c2ecf20Sopenharmony_ci		break;
508c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_NUM_FREE_STREAMS:
518c2ecf20Sopenharmony_ci		param->value = vmw_overlay_num_free_overlays(dev_priv);
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_3D:
548c2ecf20Sopenharmony_ci		param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_HW_CAPS:
578c2ecf20Sopenharmony_ci		param->value = dev_priv->capabilities;
588c2ecf20Sopenharmony_ci		break;
598c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_HW_CAPS2:
608c2ecf20Sopenharmony_ci		param->value = dev_priv->capabilities2;
618c2ecf20Sopenharmony_ci		break;
628c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_FIFO_CAPS:
638c2ecf20Sopenharmony_ci		param->value = dev_priv->fifo.capabilities;
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_MAX_FB_SIZE:
668c2ecf20Sopenharmony_ci		param->value = dev_priv->prim_bb_mem;
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_FIFO_HW_VERSION:
698c2ecf20Sopenharmony_ci	{
708c2ecf20Sopenharmony_ci		u32 *fifo_mem = dev_priv->mmio_virt;
718c2ecf20Sopenharmony_ci		const struct vmw_fifo_state *fifo = &dev_priv->fifo;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) {
748c2ecf20Sopenharmony_ci			param->value = SVGA3D_HWVERSION_WS8_B1;
758c2ecf20Sopenharmony_ci			break;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		param->value =
798c2ecf20Sopenharmony_ci			vmw_mmio_read(fifo_mem +
808c2ecf20Sopenharmony_ci				      ((fifo->capabilities &
818c2ecf20Sopenharmony_ci					SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ?
828c2ecf20Sopenharmony_ci				       SVGA_FIFO_3D_HWVERSION_REVISED :
838c2ecf20Sopenharmony_ci				       SVGA_FIFO_3D_HWVERSION));
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_MAX_SURF_MEMORY:
878c2ecf20Sopenharmony_ci		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS) &&
888c2ecf20Sopenharmony_ci		    !vmw_fp->gb_aware)
898c2ecf20Sopenharmony_ci			param->value = dev_priv->max_mob_pages * PAGE_SIZE / 2;
908c2ecf20Sopenharmony_ci		else
918c2ecf20Sopenharmony_ci			param->value = dev_priv->memory_size;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_3D_CAPS_SIZE:
948c2ecf20Sopenharmony_ci		if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS) &&
958c2ecf20Sopenharmony_ci		    vmw_fp->gb_aware)
968c2ecf20Sopenharmony_ci			param->value = SVGA3D_DEVCAP_MAX * sizeof(uint32_t);
978c2ecf20Sopenharmony_ci		else if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS)
988c2ecf20Sopenharmony_ci			param->value = sizeof(struct svga_3d_compat_cap) +
998c2ecf20Sopenharmony_ci				sizeof(uint32_t);
1008c2ecf20Sopenharmony_ci		else
1018c2ecf20Sopenharmony_ci			param->value = (SVGA_FIFO_3D_CAPS_LAST -
1028c2ecf20Sopenharmony_ci					SVGA_FIFO_3D_CAPS + 1) *
1038c2ecf20Sopenharmony_ci				sizeof(uint32_t);
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_MAX_MOB_MEMORY:
1068c2ecf20Sopenharmony_ci		vmw_fp->gb_aware = true;
1078c2ecf20Sopenharmony_ci		param->value = dev_priv->max_mob_pages * PAGE_SIZE;
1088c2ecf20Sopenharmony_ci		break;
1098c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_MAX_MOB_SIZE:
1108c2ecf20Sopenharmony_ci		param->value = dev_priv->max_mob_size;
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_SCREEN_TARGET:
1138c2ecf20Sopenharmony_ci		param->value =
1148c2ecf20Sopenharmony_ci			(dev_priv->active_display_unit == vmw_du_screen_target);
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_DX:
1178c2ecf20Sopenharmony_ci		param->value = has_sm4_context(dev_priv);
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_SM4_1:
1208c2ecf20Sopenharmony_ci		param->value = has_sm4_1_context(dev_priv);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case DRM_VMW_PARAM_SM5:
1238c2ecf20Sopenharmony_ci		param->value = has_sm5_context(dev_priv);
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	default:
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic u32 vmw_mask_legacy_multisample(unsigned int cap, u32 fmt_value)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * A version of user-space exists which use MULTISAMPLE_MASKABLESAMPLES
1368c2ecf20Sopenharmony_ci	 * to check the sample count supported by virtual device. Since there
1378c2ecf20Sopenharmony_ci	 * never was support for multisample count for backing MOB return 0.
1388c2ecf20Sopenharmony_ci	 *
1398c2ecf20Sopenharmony_ci	 * MULTISAMPLE_MASKABLESAMPLES devcap is marked as deprecated by virtual
1408c2ecf20Sopenharmony_ci	 * device.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	if (cap == SVGA3D_DEVCAP_DEAD5)
1438c2ecf20Sopenharmony_ci		return 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return fmt_value;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int vmw_fill_compat_cap(struct vmw_private *dev_priv, void *bounce,
1498c2ecf20Sopenharmony_ci			       size_t size)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct svga_3d_compat_cap *compat_cap =
1528c2ecf20Sopenharmony_ci		(struct svga_3d_compat_cap *) bounce;
1538c2ecf20Sopenharmony_ci	unsigned int i;
1548c2ecf20Sopenharmony_ci	size_t pair_offset = offsetof(struct svga_3d_compat_cap, pairs);
1558c2ecf20Sopenharmony_ci	unsigned int max_size;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (size < pair_offset)
1588c2ecf20Sopenharmony_ci		return -EINVAL;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	max_size = (size - pair_offset) / sizeof(SVGA3dCapPair);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (max_size > SVGA3D_DEVCAP_MAX)
1638c2ecf20Sopenharmony_ci		max_size = SVGA3D_DEVCAP_MAX;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	compat_cap->header.length =
1668c2ecf20Sopenharmony_ci		(pair_offset + max_size * sizeof(SVGA3dCapPair)) / sizeof(u32);
1678c2ecf20Sopenharmony_ci	compat_cap->header.type = SVGA3DCAPS_RECORD_DEVCAPS;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	spin_lock(&dev_priv->cap_lock);
1708c2ecf20Sopenharmony_ci	for (i = 0; i < max_size; ++i) {
1718c2ecf20Sopenharmony_ci		vmw_write(dev_priv, SVGA_REG_DEV_CAP, i);
1728c2ecf20Sopenharmony_ci		compat_cap->pairs[i][0] = i;
1738c2ecf20Sopenharmony_ci		compat_cap->pairs[i][1] = vmw_mask_legacy_multisample
1748c2ecf20Sopenharmony_ci			(i, vmw_read(dev_priv, SVGA_REG_DEV_CAP));
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci	spin_unlock(&dev_priv->cap_lock);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciint vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
1838c2ecf20Sopenharmony_ci			 struct drm_file *file_priv)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct drm_vmw_get_3d_cap_arg *arg =
1868c2ecf20Sopenharmony_ci		(struct drm_vmw_get_3d_cap_arg *) data;
1878c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
1888c2ecf20Sopenharmony_ci	uint32_t size;
1898c2ecf20Sopenharmony_ci	u32 *fifo_mem;
1908c2ecf20Sopenharmony_ci	void __user *buffer = (void __user *)((unsigned long)(arg->buffer));
1918c2ecf20Sopenharmony_ci	void *bounce;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci	bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
1948c2ecf20Sopenharmony_ci	struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
1978c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Illegal GET_3D_CAP argument.\n");
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (gb_objects && vmw_fp->gb_aware)
2028c2ecf20Sopenharmony_ci		size = SVGA3D_DEVCAP_MAX * sizeof(uint32_t);
2038c2ecf20Sopenharmony_ci	else if (gb_objects)
2048c2ecf20Sopenharmony_ci		size = sizeof(struct svga_3d_compat_cap) + sizeof(uint32_t);
2058c2ecf20Sopenharmony_ci	else
2068c2ecf20Sopenharmony_ci		size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) *
2078c2ecf20Sopenharmony_ci			sizeof(uint32_t);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (arg->max_size < size)
2108c2ecf20Sopenharmony_ci		size = arg->max_size;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	bounce = vzalloc(size);
2138c2ecf20Sopenharmony_ci	if (unlikely(bounce == NULL)) {
2148c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate bounce buffer for 3D caps.\n");
2158c2ecf20Sopenharmony_ci		return -ENOMEM;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (gb_objects && vmw_fp->gb_aware) {
2198c2ecf20Sopenharmony_ci		int i, num;
2208c2ecf20Sopenharmony_ci		uint32_t *bounce32 = (uint32_t *) bounce;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		num = size / sizeof(uint32_t);
2238c2ecf20Sopenharmony_ci		if (num > SVGA3D_DEVCAP_MAX)
2248c2ecf20Sopenharmony_ci			num = SVGA3D_DEVCAP_MAX;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		spin_lock(&dev_priv->cap_lock);
2278c2ecf20Sopenharmony_ci		for (i = 0; i < num; ++i) {
2288c2ecf20Sopenharmony_ci			vmw_write(dev_priv, SVGA_REG_DEV_CAP, i);
2298c2ecf20Sopenharmony_ci			*bounce32++ = vmw_mask_legacy_multisample
2308c2ecf20Sopenharmony_ci				(i, vmw_read(dev_priv, SVGA_REG_DEV_CAP));
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci		spin_unlock(&dev_priv->cap_lock);
2338c2ecf20Sopenharmony_ci	} else if (gb_objects) {
2348c2ecf20Sopenharmony_ci		ret = vmw_fill_compat_cap(dev_priv, bounce, size);
2358c2ecf20Sopenharmony_ci		if (unlikely(ret != 0))
2368c2ecf20Sopenharmony_ci			goto out_err;
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		fifo_mem = dev_priv->mmio_virt;
2398c2ecf20Sopenharmony_ci		memcpy(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ret = copy_to_user(buffer, bounce, size);
2438c2ecf20Sopenharmony_ci	if (ret)
2448c2ecf20Sopenharmony_ci		ret = -EFAULT;
2458c2ecf20Sopenharmony_ciout_err:
2468c2ecf20Sopenharmony_ci	vfree(bounce);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
2498c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to report 3D caps info.\n");
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return ret;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciint vmw_present_ioctl(struct drm_device *dev, void *data,
2558c2ecf20Sopenharmony_ci		      struct drm_file *file_priv)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
2588c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
2598c2ecf20Sopenharmony_ci	struct drm_vmw_present_arg *arg =
2608c2ecf20Sopenharmony_ci		(struct drm_vmw_present_arg *)data;
2618c2ecf20Sopenharmony_ci	struct vmw_surface *surface;
2628c2ecf20Sopenharmony_ci	struct drm_vmw_rect __user *clips_ptr;
2638c2ecf20Sopenharmony_ci	struct drm_vmw_rect *clips = NULL;
2648c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb;
2658c2ecf20Sopenharmony_ci	struct vmw_framebuffer *vfb;
2668c2ecf20Sopenharmony_ci	struct vmw_resource *res;
2678c2ecf20Sopenharmony_ci	uint32_t num_clips;
2688c2ecf20Sopenharmony_ci	int ret;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	num_clips = arg->num_clips;
2718c2ecf20Sopenharmony_ci	clips_ptr = (struct drm_vmw_rect __user *)(unsigned long)arg->clips_ptr;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (unlikely(num_clips == 0))
2748c2ecf20Sopenharmony_ci		return 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (clips_ptr == NULL) {
2778c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Variable clips_ptr must be specified.\n");
2788c2ecf20Sopenharmony_ci		ret = -EINVAL;
2798c2ecf20Sopenharmony_ci		goto out_clips;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
2838c2ecf20Sopenharmony_ci	if (clips == NULL) {
2848c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate clip rect list.\n");
2858c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2868c2ecf20Sopenharmony_ci		goto out_clips;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
2908c2ecf20Sopenharmony_ci	if (ret) {
2918c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to copy clip rects from userspace.\n");
2928c2ecf20Sopenharmony_ci		ret = -EFAULT;
2938c2ecf20Sopenharmony_ci		goto out_no_copy;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	drm_modeset_lock_all(dev);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	fb = drm_framebuffer_lookup(dev, file_priv, arg->fb_id);
2998c2ecf20Sopenharmony_ci	if (!fb) {
3008c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Invalid framebuffer id.\n");
3018c2ecf20Sopenharmony_ci		ret = -ENOENT;
3028c2ecf20Sopenharmony_ci		goto out_no_fb;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci	vfb = vmw_framebuffer_to_vfb(fb);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ret = ttm_read_lock(&dev_priv->reservation_sem, true);
3078c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
3088c2ecf20Sopenharmony_ci		goto out_no_ttm_lock;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg->sid,
3118c2ecf20Sopenharmony_ci					      user_surface_converter,
3128c2ecf20Sopenharmony_ci					      &res);
3138c2ecf20Sopenharmony_ci	if (ret)
3148c2ecf20Sopenharmony_ci		goto out_no_surface;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	surface = vmw_res_to_srf(res);
3178c2ecf20Sopenharmony_ci	ret = vmw_kms_present(dev_priv, file_priv,
3188c2ecf20Sopenharmony_ci			      vfb, surface, arg->sid,
3198c2ecf20Sopenharmony_ci			      arg->dest_x, arg->dest_y,
3208c2ecf20Sopenharmony_ci			      clips, num_clips);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* vmw_user_surface_lookup takes one ref so does new_fb */
3238c2ecf20Sopenharmony_ci	vmw_surface_unreference(&surface);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ciout_no_surface:
3268c2ecf20Sopenharmony_ci	ttm_read_unlock(&dev_priv->reservation_sem);
3278c2ecf20Sopenharmony_ciout_no_ttm_lock:
3288c2ecf20Sopenharmony_ci	drm_framebuffer_put(fb);
3298c2ecf20Sopenharmony_ciout_no_fb:
3308c2ecf20Sopenharmony_ci	drm_modeset_unlock_all(dev);
3318c2ecf20Sopenharmony_ciout_no_copy:
3328c2ecf20Sopenharmony_ci	kfree(clips);
3338c2ecf20Sopenharmony_ciout_clips:
3348c2ecf20Sopenharmony_ci	return ret;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ciint vmw_present_readback_ioctl(struct drm_device *dev, void *data,
3388c2ecf20Sopenharmony_ci			       struct drm_file *file_priv)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv = vmw_priv(dev);
3418c2ecf20Sopenharmony_ci	struct drm_vmw_present_readback_arg *arg =
3428c2ecf20Sopenharmony_ci		(struct drm_vmw_present_readback_arg *)data;
3438c2ecf20Sopenharmony_ci	struct drm_vmw_fence_rep __user *user_fence_rep =
3448c2ecf20Sopenharmony_ci		(struct drm_vmw_fence_rep __user *)
3458c2ecf20Sopenharmony_ci		(unsigned long)arg->fence_rep;
3468c2ecf20Sopenharmony_ci	struct drm_vmw_rect __user *clips_ptr;
3478c2ecf20Sopenharmony_ci	struct drm_vmw_rect *clips = NULL;
3488c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb;
3498c2ecf20Sopenharmony_ci	struct vmw_framebuffer *vfb;
3508c2ecf20Sopenharmony_ci	uint32_t num_clips;
3518c2ecf20Sopenharmony_ci	int ret;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	num_clips = arg->num_clips;
3548c2ecf20Sopenharmony_ci	clips_ptr = (struct drm_vmw_rect __user *)(unsigned long)arg->clips_ptr;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (unlikely(num_clips == 0))
3578c2ecf20Sopenharmony_ci		return 0;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (clips_ptr == NULL) {
3608c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Argument clips_ptr must be specified.\n");
3618c2ecf20Sopenharmony_ci		ret = -EINVAL;
3628c2ecf20Sopenharmony_ci		goto out_clips;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
3668c2ecf20Sopenharmony_ci	if (clips == NULL) {
3678c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to allocate clip rect list.\n");
3688c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3698c2ecf20Sopenharmony_ci		goto out_clips;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
3738c2ecf20Sopenharmony_ci	if (ret) {
3748c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to copy clip rects from userspace.\n");
3758c2ecf20Sopenharmony_ci		ret = -EFAULT;
3768c2ecf20Sopenharmony_ci		goto out_no_copy;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	drm_modeset_lock_all(dev);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	fb = drm_framebuffer_lookup(dev, file_priv, arg->fb_id);
3828c2ecf20Sopenharmony_ci	if (!fb) {
3838c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Invalid framebuffer id.\n");
3848c2ecf20Sopenharmony_ci		ret = -ENOENT;
3858c2ecf20Sopenharmony_ci		goto out_no_fb;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	vfb = vmw_framebuffer_to_vfb(fb);
3898c2ecf20Sopenharmony_ci	if (!vfb->bo) {
3908c2ecf20Sopenharmony_ci		VMW_DEBUG_USER("Framebuffer not buffer backed.\n");
3918c2ecf20Sopenharmony_ci		ret = -EINVAL;
3928c2ecf20Sopenharmony_ci		goto out_no_ttm_lock;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = ttm_read_lock(&dev_priv->reservation_sem, true);
3968c2ecf20Sopenharmony_ci	if (unlikely(ret != 0))
3978c2ecf20Sopenharmony_ci		goto out_no_ttm_lock;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = vmw_kms_readback(dev_priv, file_priv,
4008c2ecf20Sopenharmony_ci			       vfb, user_fence_rep,
4018c2ecf20Sopenharmony_ci			       clips, num_clips);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ttm_read_unlock(&dev_priv->reservation_sem);
4048c2ecf20Sopenharmony_ciout_no_ttm_lock:
4058c2ecf20Sopenharmony_ci	drm_framebuffer_put(fb);
4068c2ecf20Sopenharmony_ciout_no_fb:
4078c2ecf20Sopenharmony_ci	drm_modeset_unlock_all(dev);
4088c2ecf20Sopenharmony_ciout_no_copy:
4098c2ecf20Sopenharmony_ci	kfree(clips);
4108c2ecf20Sopenharmony_ciout_clips:
4118c2ecf20Sopenharmony_ci	return ret;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/**
4168c2ecf20Sopenharmony_ci * vmw_fops_poll - wrapper around the drm_poll function
4178c2ecf20Sopenharmony_ci *
4188c2ecf20Sopenharmony_ci * @filp: See the linux fops poll documentation.
4198c2ecf20Sopenharmony_ci * @wait: See the linux fops poll documentation.
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci * Wrapper around the drm_poll function that makes sure the device is
4228c2ecf20Sopenharmony_ci * processing the fifo if drm_poll decides to wait.
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_ci__poll_t vmw_fops_poll(struct file *filp, struct poll_table_struct *wait)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct drm_file *file_priv = filp->private_data;
4278c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv =
4288c2ecf20Sopenharmony_ci		vmw_priv(file_priv->minor->dev);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
4318c2ecf20Sopenharmony_ci	return drm_poll(filp, wait);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci/**
4368c2ecf20Sopenharmony_ci * vmw_fops_read - wrapper around the drm_read function
4378c2ecf20Sopenharmony_ci *
4388c2ecf20Sopenharmony_ci * @filp: See the linux fops read documentation.
4398c2ecf20Sopenharmony_ci * @buffer: See the linux fops read documentation.
4408c2ecf20Sopenharmony_ci * @count: See the linux fops read documentation.
4418c2ecf20Sopenharmony_ci * offset: See the linux fops read documentation.
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * Wrapper around the drm_read function that makes sure the device is
4448c2ecf20Sopenharmony_ci * processing the fifo if drm_read decides to wait.
4458c2ecf20Sopenharmony_ci */
4468c2ecf20Sopenharmony_cissize_t vmw_fops_read(struct file *filp, char __user *buffer,
4478c2ecf20Sopenharmony_ci		      size_t count, loff_t *offset)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct drm_file *file_priv = filp->private_data;
4508c2ecf20Sopenharmony_ci	struct vmw_private *dev_priv =
4518c2ecf20Sopenharmony_ci		vmw_priv(file_priv->minor->dev);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
4548c2ecf20Sopenharmony_ci	return drm_read(filp, buffer, count, offset);
4558c2ecf20Sopenharmony_ci}
456