vmwgfx_ioctl.c 9.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/**************************************************************************
 *
 * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "vmwgfx_drv.h"
29
#include <drm/vmwgfx_drm.h>
30
#include "vmwgfx_kms.h"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

int vmw_getparam_ioctl(struct drm_device *dev, void *data,
		       struct drm_file *file_priv)
{
	struct vmw_private *dev_priv = vmw_priv(dev);
	struct drm_vmw_getparam_arg *param =
	    (struct drm_vmw_getparam_arg *)data;

	switch (param->param) {
	case DRM_VMW_PARAM_NUM_STREAMS:
		param->value = vmw_overlay_num_overlays(dev_priv);
		break;
	case DRM_VMW_PARAM_NUM_FREE_STREAMS:
		param->value = vmw_overlay_num_free_overlays(dev_priv);
		break;
	case DRM_VMW_PARAM_3D:
47
		param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;
48
		break;
49 50 51 52 53 54
	case DRM_VMW_PARAM_HW_CAPS:
		param->value = dev_priv->capabilities;
		break;
	case DRM_VMW_PARAM_FIFO_CAPS:
		param->value = dev_priv->fifo.capabilities;
		break;
55
	case DRM_VMW_PARAM_MAX_FB_SIZE:
56
		param->value = dev_priv->prim_bb_mem;
57
		break;
58 59 60
	case DRM_VMW_PARAM_FIFO_HW_VERSION:
	{
		__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
61
		const struct vmw_fifo_state *fifo = &dev_priv->fifo;
62

63 64 65 66 67 68
		param->value =
			ioread32(fifo_mem +
				 ((fifo->capabilities &
				   SVGA_FIFO_CAP_3D_HWVERSION_REVISED) ?
				  SVGA_FIFO_3D_HWVERSION_REVISED :
				  SVGA_FIFO_3D_HWVERSION));
69 70
		break;
	}
71 72 73 74 75 76 77 78 79 80 81
	case DRM_VMW_PARAM_MAX_SURF_MEMORY:
		param->value = dev_priv->memory_size;
		break;
	case DRM_VMW_PARAM_3D_CAPS_SIZE:
		if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS)
			param->value = SVGA3D_DEVCAP_MAX;
		else
			param->value = (SVGA_FIFO_3D_CAPS_LAST -
					SVGA_FIFO_3D_CAPS + 1);
		param->value *= sizeof(uint32_t);
		break;
82 83 84
	case DRM_VMW_PARAM_MAX_MOB_MEMORY:
		param->value = dev_priv->max_mob_pages * PAGE_SIZE;
		break;
85 86 87 88 89 90 91 92
	default:
		DRM_ERROR("Illegal vmwgfx get param request: %d\n",
			  param->param);
		return -EINVAL;
	}

	return 0;
}
93 94 95 96 97 98 99 100 101 102 103 104 105


int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
{
	struct drm_vmw_get_3d_cap_arg *arg =
		(struct drm_vmw_get_3d_cap_arg *) data;
	struct vmw_private *dev_priv = vmw_priv(dev);
	uint32_t size;
	__le32 __iomem *fifo_mem;
	void __user *buffer = (void __user *)((unsigned long)(arg->buffer));
	void *bounce;
	int ret;
106
	bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
107 108 109 110 111 112

	if (unlikely(arg->pad64 != 0)) {
		DRM_ERROR("Illegal GET_3D_CAP argument.\n");
		return -EINVAL;
	}

113 114 115 116 117 118
	if (gb_objects)
		size = SVGA3D_DEVCAP_MAX;
	else
		size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1);

	size *= sizeof(uint32_t);
119 120 121 122 123 124 125 126 127 128

	if (arg->max_size < size)
		size = arg->max_size;

	bounce = vmalloc(size);
	if (unlikely(bounce == NULL)) {
		DRM_ERROR("Failed to allocate bounce buffer for 3D caps.\n");
		return -ENOMEM;
	}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	if (gb_objects) {
		int i;
		uint32_t *bounce32 = (uint32_t *) bounce;

		mutex_lock(&dev_priv->hw_mutex);
		for (i = 0; i < SVGA3D_DEVCAP_MAX; ++i) {
			vmw_write(dev_priv, SVGA_REG_DEV_CAP, i);
			*bounce32++ = vmw_read(dev_priv, SVGA_REG_DEV_CAP);
		}
		mutex_unlock(&dev_priv->hw_mutex);

	} else {

		fifo_mem = dev_priv->mmio_virt;
		memcpy_fromio(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size);
	}
145 146

	ret = copy_to_user(buffer, bounce, size);
147 148
	if (ret)
		ret = -EFAULT;
149 150 151 152 153 154 155
	vfree(bounce);

	if (unlikely(ret != 0))
		DRM_ERROR("Failed to report 3D caps info.\n");

	return ret;
}
156 157 158 159 160 161 162 163 164 165 166 167

int vmw_present_ioctl(struct drm_device *dev, void *data,
		      struct drm_file *file_priv)
{
	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
	struct vmw_private *dev_priv = vmw_priv(dev);
	struct drm_vmw_present_arg *arg =
		(struct drm_vmw_present_arg *)data;
	struct vmw_surface *surface;
	struct vmw_master *vmaster = vmw_master(file_priv->master);
	struct drm_vmw_rect __user *clips_ptr;
	struct drm_vmw_rect *clips = NULL;
168
	struct drm_framebuffer *fb;
169
	struct vmw_framebuffer *vfb;
170
	struct vmw_resource *res;
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	uint32_t num_clips;
	int ret;

	num_clips = arg->num_clips;
	clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;

	if (unlikely(num_clips == 0))
		return 0;

	if (clips_ptr == NULL) {
		DRM_ERROR("Variable clips_ptr must be specified.\n");
		ret = -EINVAL;
		goto out_clips;
	}

186
	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
187 188 189 190 191 192 193 194 195
	if (clips == NULL) {
		DRM_ERROR("Failed to allocate clip rect list.\n");
		ret = -ENOMEM;
		goto out_clips;
	}

	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
	if (ret) {
		DRM_ERROR("Failed to copy clip rects from userspace.\n");
196
		ret = -EFAULT;
197 198 199
		goto out_no_copy;
	}

200
	drm_modeset_lock_all(dev);
201

202 203
	fb = drm_framebuffer_lookup(dev, arg->fb_id);
	if (!fb) {
204
		DRM_ERROR("Invalid framebuffer id.\n");
205
		ret = -ENOENT;
206 207
		goto out_no_fb;
	}
208
	vfb = vmw_framebuffer_to_vfb(fb);
209 210 211 212 213

	ret = ttm_read_lock(&vmaster->lock, true);
	if (unlikely(ret != 0))
		goto out_no_ttm_lock;

214 215 216
	ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg->sid,
					      user_surface_converter,
					      &res);
217 218 219
	if (ret)
		goto out_no_surface;

220
	surface = vmw_res_to_srf(res);
221 222 223 224 225 226 227 228 229 230 231
	ret = vmw_kms_present(dev_priv, file_priv,
			      vfb, surface, arg->sid,
			      arg->dest_x, arg->dest_y,
			      clips, num_clips);

	/* vmw_user_surface_lookup takes one ref so does new_fb */
	vmw_surface_unreference(&surface);

out_no_surface:
	ttm_read_unlock(&vmaster->lock);
out_no_ttm_lock:
232
	drm_framebuffer_unreference(fb);
233
out_no_fb:
234
	drm_modeset_unlock_all(dev);
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
out_no_copy:
	kfree(clips);
out_clips:
	return ret;
}

int vmw_present_readback_ioctl(struct drm_device *dev, void *data,
			       struct drm_file *file_priv)
{
	struct vmw_private *dev_priv = vmw_priv(dev);
	struct drm_vmw_present_readback_arg *arg =
		(struct drm_vmw_present_readback_arg *)data;
	struct drm_vmw_fence_rep __user *user_fence_rep =
		(struct drm_vmw_fence_rep __user *)
		(unsigned long)arg->fence_rep;
	struct vmw_master *vmaster = vmw_master(file_priv->master);
	struct drm_vmw_rect __user *clips_ptr;
	struct drm_vmw_rect *clips = NULL;
253
	struct drm_framebuffer *fb;
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
	struct vmw_framebuffer *vfb;
	uint32_t num_clips;
	int ret;

	num_clips = arg->num_clips;
	clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr;

	if (unlikely(num_clips == 0))
		return 0;

	if (clips_ptr == NULL) {
		DRM_ERROR("Argument clips_ptr must be specified.\n");
		ret = -EINVAL;
		goto out_clips;
	}

270
	clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
271 272 273 274 275 276 277 278 279
	if (clips == NULL) {
		DRM_ERROR("Failed to allocate clip rect list.\n");
		ret = -ENOMEM;
		goto out_clips;
	}

	ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips));
	if (ret) {
		DRM_ERROR("Failed to copy clip rects from userspace.\n");
280
		ret = -EFAULT;
281 282 283
		goto out_no_copy;
	}

284
	drm_modeset_lock_all(dev);
285

286 287
	fb = drm_framebuffer_lookup(dev, arg->fb_id);
	if (!fb) {
288
		DRM_ERROR("Invalid framebuffer id.\n");
289
		ret = -ENOENT;
290 291 292
		goto out_no_fb;
	}

293
	vfb = vmw_framebuffer_to_vfb(fb);
294 295 296
	if (!vfb->dmabuf) {
		DRM_ERROR("Framebuffer not dmabuf backed.\n");
		ret = -EINVAL;
297
		goto out_no_ttm_lock;
298 299 300 301 302 303 304 305 306 307 308 309
	}

	ret = ttm_read_lock(&vmaster->lock, true);
	if (unlikely(ret != 0))
		goto out_no_ttm_lock;

	ret = vmw_kms_readback(dev_priv, file_priv,
			       vfb, user_fence_rep,
			       clips, num_clips);

	ttm_read_unlock(&vmaster->lock);
out_no_ttm_lock:
310
	drm_framebuffer_unreference(fb);
311
out_no_fb:
312
	drm_modeset_unlock_all(dev);
313 314 315 316 317
out_no_copy:
	kfree(clips);
out_clips:
	return ret;
}
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360


/**
 * vmw_fops_poll - wrapper around the drm_poll function
 *
 * @filp: See the linux fops poll documentation.
 * @wait: See the linux fops poll documentation.
 *
 * Wrapper around the drm_poll function that makes sure the device is
 * processing the fifo if drm_poll decides to wait.
 */
unsigned int vmw_fops_poll(struct file *filp, struct poll_table_struct *wait)
{
	struct drm_file *file_priv = filp->private_data;
	struct vmw_private *dev_priv =
		vmw_priv(file_priv->minor->dev);

	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
	return drm_poll(filp, wait);
}


/**
 * vmw_fops_read - wrapper around the drm_read function
 *
 * @filp: See the linux fops read documentation.
 * @buffer: See the linux fops read documentation.
 * @count: See the linux fops read documentation.
 * offset: See the linux fops read documentation.
 *
 * Wrapper around the drm_read function that makes sure the device is
 * processing the fifo if drm_read decides to wait.
 */
ssize_t vmw_fops_read(struct file *filp, char __user *buffer,
		      size_t count, loff_t *offset)
{
	struct drm_file *file_priv = filp->private_data;
	struct vmw_private *dev_priv =
		vmw_priv(file_priv->minor->dev);

	vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
	return drm_read(filp, buffer, count, offset);
}