vmwgfx_drmi.c revision 3bfa90b6
1/* 2 * Copyright 2011 VMWare, Inc. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 * Author: Jakob Bornecrantz <wallbraker@gmail.com> 26 * Author: Thomas Hellstrom <thellstrom@vmware.com> 27 */ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#include <stdint.h> 34#include <errno.h> 35#include <sys/mman.h> 36#include "vmwgfx_drm.h" 37#include <xf86drm.h> 38#include "vmwgfx_drmi.h" 39 40#define uint32 uint32_t 41#define int32 int32_t 42#define uint16 uint16_t 43#define uint8 uint8_t 44 45#include "svga3d_reg.h" 46#include "vmwgfx_driver.h" 47 48static int 49vmwgfx_fence_wait(int drm_fd, uint32_t handle, Bool unref) 50{ 51 struct drm_vmw_fence_wait_arg farg; 52 memset(&farg, 0, sizeof(farg)); 53 54 farg.handle = handle; 55 farg.flags = DRM_VMW_FENCE_FLAG_EXEC; 56 farg.timeout_us = 10*1000000; 57 farg.cookie_valid = 0; 58 59 if (unref) 60 farg.wait_options |= DRM_VMW_WAIT_OPTION_UNREF; 61 62 return drmCommandWriteRead(drm_fd, DRM_VMW_FENCE_WAIT, &farg, 63 sizeof(farg)); 64} 65 66static void 67vmwgfx_fence_unref(int drm_fd, uint32_t handle) 68{ 69 struct drm_vmw_fence_arg farg; 70 memset(&farg, 0, sizeof(farg)); 71 72 farg.handle = handle; 73 74 (void) drmCommandWrite(drm_fd, DRM_VMW_FENCE_UNREF, &farg, 75 sizeof(farg)); 76} 77 78 79int 80vmwgfx_present_readback(int drm_fd, uint32_t fb_id, RegionPtr region) 81{ 82 BoxPtr clips = REGION_RECTS(region); 83 unsigned int num_clips = REGION_NUM_RECTS(region); 84 struct drm_vmw_fence_rep rep; 85 struct drm_vmw_present_readback_arg arg; 86 int ret; 87 unsigned i; 88 struct drm_vmw_rect *rects, *r; 89 90 rects = calloc(num_clips, sizeof(*rects)); 91 if (!rects) { 92 LogMessage(X_ERROR, "Failed to alloc cliprects for " 93 "present readback.\n"); 94 return -1; 95 } 96 97 memset(&arg, 0, sizeof(arg)); 98 memset(&rep, 0, sizeof(rep)); 99 100 arg.fb_id = fb_id; 101 arg.num_clips = num_clips; 102 arg.clips_ptr = (unsigned long) rects; 103 arg.fence_rep = (unsigned long) &rep; 104 rep.error = -EFAULT; 105 106 for (i = 0, r = rects; i < num_clips; ++i, ++r, ++clips) { 107 r->x = clips->x1; 108 r->y = clips->y1; 109 r->w = clips->x2 - clips->x1; 110 r->h = clips->y2 - clips->y1; 111 } 112 113 ret = drmCommandWrite(drm_fd, DRM_VMW_PRESENT_READBACK, &arg, sizeof(arg)); 114 if (ret) 115 LogMessage(X_ERROR, "Present readback error %s.\n", strerror(-ret)); 116 free(rects); 117 118 /* 119 * Sync to avoid racing with Xorg SW rendering. 120 */ 121 122 if (rep.error == 0) { 123 ret = vmwgfx_fence_wait(drm_fd, rep.handle, TRUE); 124 if (ret) { 125 LogMessage(X_ERROR, "Present readback fence wait error %s.\n", 126 strerror(-ret)); 127 vmwgfx_fence_unref(drm_fd, rep.handle); 128 } 129 } 130 131 return 0; 132} 133 134 135int 136vmwgfx_present(int drm_fd, uint32_t fb_id, unsigned int dst_x, 137 unsigned int dst_y, RegionPtr region, uint32_t handle) 138{ 139 BoxPtr clips = REGION_RECTS(region); 140 unsigned int num_clips = REGION_NUM_RECTS(region); 141 struct drm_vmw_present_arg arg; 142 unsigned int i; 143 struct drm_vmw_rect *rects, *r; 144 int ret; 145 146 if (num_clips == 0) 147 return 0; 148 149 rects = calloc(num_clips, sizeof(*rects)); 150 if (!rects) { 151 LogMessage(X_ERROR, "Failed to alloc cliprects for " 152 "present.\n"); 153 return -1; 154 } 155 156 memset(&arg, 0, sizeof(arg)); 157 arg.fb_id = fb_id; 158 arg.sid = handle; 159 arg.dest_x = dst_x; 160 arg.dest_y = dst_y; 161 arg.num_clips = num_clips; 162 arg.clips_ptr = (unsigned long) rects; 163 164 for (i = 0, r = rects; i < num_clips; ++i, ++r, ++clips) { 165 r->x = clips->x1; 166 r->y = clips->y1; 167 r->w = clips->x2 - clips->x1; 168 r->h = clips->y2 - clips->y1; 169 } 170 171 ret = drmCommandWrite(drm_fd, DRM_VMW_PRESENT, &arg, sizeof(arg)); 172 if (ret) { 173 LogMessage(X_ERROR, "Present error %s.\n", strerror(-ret)); 174 } 175 176 free(rects); 177 return ((ret != 0) ? -1 : 0); 178} 179 180 181struct vmwgfx_int_dmabuf { 182 struct vmwgfx_dmabuf buf; 183 uint64_t map_handle; 184 uint64_t sync_handle; 185 int sync_valid; 186 int drm_fd; 187 uint32_t map_count; 188 void *addr; 189}; 190 191static inline struct vmwgfx_int_dmabuf * 192vmwgfx_int_dmabuf(struct vmwgfx_dmabuf *buf) 193{ 194 return (struct vmwgfx_int_dmabuf *) buf; 195} 196 197struct vmwgfx_dmabuf* 198vmwgfx_dmabuf_alloc(int drm_fd, size_t size) 199{ 200 union drm_vmw_alloc_dmabuf_arg arg; 201 struct vmwgfx_dmabuf *buf; 202 struct vmwgfx_int_dmabuf *ibuf; 203 int ret; 204 205 ibuf = calloc(1, sizeof(*ibuf)); 206 if (!ibuf) 207 return NULL; 208 209 buf = &ibuf->buf; 210 memset(&arg, 0, sizeof(arg)); 211 arg.req.size = size; 212 213 ret = drmCommandWriteRead(drm_fd, DRM_VMW_ALLOC_DMABUF, &arg, 214 sizeof(arg)); 215 if (ret) 216 goto out_kernel_fail; 217 218 ibuf = vmwgfx_int_dmabuf(buf); 219 ibuf->map_handle = arg.rep.map_handle; 220 ibuf->drm_fd = drm_fd; 221 buf->handle = arg.rep.handle; 222 buf->gmr_id = arg.rep.cur_gmr_id; 223 buf->gmr_offset = arg.rep.cur_gmr_offset; 224 buf->size = size; 225 226 return buf; 227 out_kernel_fail: 228 free(buf); 229 return NULL; 230} 231 232void * 233vmwgfx_dmabuf_map(struct vmwgfx_dmabuf *buf) 234{ 235 struct vmwgfx_int_dmabuf *ibuf = vmwgfx_int_dmabuf(buf); 236 237 if (ibuf->addr) 238 return ibuf->addr; 239 240 ibuf->addr = mmap(NULL, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, 241 ibuf->drm_fd, ibuf->map_handle); 242 243 if (ibuf->addr == MAP_FAILED) { 244 ibuf->addr = NULL; 245 return NULL; 246 } 247 248 ibuf->map_count++; 249 return ibuf->addr; 250} 251 252void 253vmwgfx_dmabuf_unmap(struct vmwgfx_dmabuf *buf) 254{ 255 struct vmwgfx_int_dmabuf *ibuf = vmwgfx_int_dmabuf(buf); 256 257 if (--ibuf->map_count) 258 return; 259 260 /* 261 * It's a pretty important performance optimzation not to call 262 * munmap here, although we should watch out for cases where we might fill 263 * the virtual memory space of the process. 264 */ 265} 266 267void 268vmwgfx_dmabuf_destroy(struct vmwgfx_dmabuf *buf) 269{ 270 struct vmwgfx_int_dmabuf *ibuf = vmwgfx_int_dmabuf(buf); 271 struct drm_vmw_unref_dmabuf_arg arg; 272 273 if (ibuf->addr) { 274 munmap(ibuf->addr, buf->size); 275 ibuf->addr = NULL; 276 } 277 278 memset(&arg, 0, sizeof(arg)); 279 arg.handle = buf->handle; 280 281 (void) drmCommandWrite(ibuf->drm_fd, DRM_VMW_UNREF_DMABUF, &arg, 282 sizeof(arg)); 283 free(buf); 284} 285 286int 287vmwgfx_dma(unsigned int host_x, unsigned int host_y, 288 RegionPtr region, struct vmwgfx_dmabuf *buf, 289 uint32_t buf_pitch, uint32_t surface_handle, int to_surface) 290{ 291 BoxPtr clips = REGION_RECTS(region); 292 unsigned int num_clips = REGION_NUM_RECTS(region); 293 struct drm_vmw_execbuf_arg arg; 294 struct drm_vmw_fence_rep rep; 295 int ret; 296 unsigned int size; 297 unsigned i; 298 SVGA3dCopyBox *cb; 299 SVGA3dCmdSurfaceDMASuffix *suffix; 300 SVGA3dCmdSurfaceDMA *body; 301 struct vmwgfx_int_dmabuf *ibuf = vmwgfx_int_dmabuf(buf); 302 303 struct { 304 SVGA3dCmdHeader header; 305 SVGA3dCmdSurfaceDMA body; 306 SVGA3dCopyBox cb; 307 } *cmd; 308 309 if (num_clips == 0) 310 return 0; 311 312 size = sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cb) + 313 sizeof(*suffix); 314 cmd = malloc(size); 315 if (!cmd) 316 return -1; 317 318 cmd->header.id = SVGA_3D_CMD_SURFACE_DMA; 319 cmd->header.size = sizeof(cmd->body) + num_clips * sizeof(cmd->cb) + 320 sizeof(*suffix); 321 cb = &cmd->cb; 322 323 suffix = (SVGA3dCmdSurfaceDMASuffix *) &cb[num_clips]; 324 suffix->suffixSize = sizeof(*suffix); 325 suffix->maximumOffset = (uint32_t) -1; 326 suffix->flags.discard = 0; 327 suffix->flags.unsynchronized = 0; 328 suffix->flags.reserved = 0; 329 330 body = &cmd->body; 331 body->guest.ptr.gmrId = buf->gmr_id; 332 body->guest.ptr.offset = buf->gmr_offset; 333 body->guest.pitch = buf_pitch; 334 body->host.sid = surface_handle; 335 body->host.face = 0; 336 body->host.mipmap = 0; 337 338 body->transfer = (to_surface ? SVGA3D_WRITE_HOST_VRAM : 339 SVGA3D_READ_HOST_VRAM); 340 341 342 for (i=0; i < num_clips; i++, cb++, clips++) { 343 cb->x = (uint16_t) clips->x1 + host_x; 344 cb->y = (uint16_t) clips->y1 + host_y; 345 cb->z = 0; 346 cb->srcx = (uint16_t) clips->x1; 347 cb->srcy = (uint16_t) clips->y1; 348 cb->srcz = 0; 349 cb->w = (uint16_t) (clips->x2 - clips->x1); 350 cb->h = (uint16_t) (clips->y2 - clips->y1); 351 cb->d = 1; 352#if 0 353 LogMessage(X_INFO, "DMA! x: %u y: %u srcx: %u srcy: %u w: %u h: %u %s\n", 354 cb->x, cb->y, cb->srcx, cb->srcy, cb->w, cb->h, 355 to_surface ? "to" : "from"); 356#endif 357 358 } 359 360 memset(&arg, 0, sizeof(arg)); 361 memset(&rep, 0, sizeof(rep)); 362 363 rep.error = -EFAULT; 364 arg.fence_rep = ((to_surface) ? 0UL : (unsigned long)&rep); 365 arg.commands = (unsigned long)cmd; 366 arg.command_size = size; 367 arg.throttle_us = 0; 368 arg.version = DRM_VMW_EXECBUF_VERSION; 369 370 ret = drmCommandWrite(ibuf->drm_fd, DRM_VMW_EXECBUF, &arg, sizeof(arg)); 371 if (ret) { 372 LogMessage(X_ERROR, "DMA error %s.\n", strerror(-ret)); 373 } 374 375 free(cmd); 376 377 if (rep.error == 0) { 378 ret = vmwgfx_fence_wait(ibuf->drm_fd, rep.handle, TRUE); 379 if (ret) { 380 LogMessage(X_ERROR, "DMA from host fence wait error %s.\n", 381 strerror(-ret)); 382 vmwgfx_fence_unref(ibuf->drm_fd, rep.handle); 383 } 384 } 385 386 return 0; 387} 388 389int 390vmwgfx_get_param(int drm_fd, uint32_t param, uint64_t *out) 391{ 392 struct drm_vmw_getparam_arg gp_arg; 393 int ret; 394 395 memset(&gp_arg, 0, sizeof(gp_arg)); 396 gp_arg.param = param; 397 ret = drmCommandWriteRead(drm_fd, DRM_VMW_GET_PARAM, 398 &gp_arg, sizeof(gp_arg)); 399 400 if (ret == 0) { 401 *out = gp_arg.value; 402 } 403 404 return ret; 405} 406 407int 408vmwgfx_num_streams(int drm_fd, uint32_t *ntot, uint32_t *nfree) 409{ 410 uint64_t v1, v2; 411 int ret; 412 413 ret = vmwgfx_get_param(drm_fd, DRM_VMW_PARAM_NUM_STREAMS, &v1); 414 if (ret) 415 return ret; 416 417 ret = vmwgfx_get_param(drm_fd, DRM_VMW_PARAM_NUM_FREE_STREAMS, &v2); 418 if (ret) 419 return ret; 420 421 *ntot = (uint32_t)v1; 422 *nfree = (uint32_t)v2; 423 424 return 0; 425} 426 427int 428vmwgfx_claim_stream(int drm_fd, uint32_t *out) 429{ 430 struct drm_vmw_stream_arg s_arg; 431 int ret; 432 433 ret = drmCommandRead(drm_fd, DRM_VMW_CLAIM_STREAM, 434 &s_arg, sizeof(s_arg)); 435 436 if (ret) 437 return -1; 438 439 *out = s_arg.stream_id; 440 return 0; 441} 442 443int 444vmwgfx_unref_stream(int drm_fd, uint32_t stream_id) 445{ 446 struct drm_vmw_stream_arg s_arg; 447 int ret; 448 449 memset(&s_arg, 0, sizeof(s_arg)); 450 s_arg.stream_id = stream_id; 451 452 ret = drmCommandWrite(drm_fd, DRM_VMW_UNREF_STREAM, 453 &s_arg, sizeof(s_arg)); 454 455 return (ret != 0) ? -1 : 0; 456} 457 458int 459vmwgfx_cursor_bypass(int drm_fd, int xhot, int yhot) 460{ 461 struct drm_vmw_cursor_bypass_arg arg; 462 int ret; 463 464 memset(&arg, 0, sizeof(arg)); 465 arg.flags = DRM_VMW_CURSOR_BYPASS_ALL; 466 arg.xhot = xhot; 467 arg.yhot = yhot; 468 469 ret = drmCommandWrite(drm_fd, DRM_VMW_CURSOR_BYPASS, 470 &arg, sizeof(arg)); 471 472 return ret; 473} 474 475int 476vmwgfx_update_gui_layout(int drm_fd, unsigned int num_rects, 477 struct drm_vmw_rect *rects) 478{ 479 struct drm_vmw_update_layout_arg arg; 480 481 memset(&arg, 0, sizeof(arg)); 482 483 arg.num_outputs = num_rects; 484 arg.rects = (unsigned long) rects; 485 486 return drmCommandWrite(drm_fd, DRM_VMW_UPDATE_LAYOUT, &arg, 487 sizeof(arg)); 488} 489 490 491int 492vmwgfx_max_fb_size(int drm_fd, size_t *size) 493{ 494 uint64_t tmp_size; 495 496 if (vmwgfx_get_param(drm_fd, DRM_VMW_PARAM_MAX_FB_SIZE, &tmp_size) != 0) 497 return -1; 498 499 *size = tmp_size; 500 501 return 0; 502} 503