Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 17431928194b36a0f88082df875e2e036da7fddf 625 lines 15 kB view raw
1/************************************************************************** 2 * 3 * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 * USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 29#include "drmP.h" 30#include "vmwgfx_drv.h" 31 32#include "ttm/ttm_placement.h" 33 34#include "svga_overlay.h" 35#include "svga_escape.h" 36 37#define VMW_MAX_NUM_STREAMS 1 38 39struct vmw_stream { 40 struct vmw_dma_buffer *buf; 41 bool claimed; 42 bool paused; 43 struct drm_vmw_control_stream_arg saved; 44}; 45 46/** 47 * Overlay control 48 */ 49struct vmw_overlay { 50 /* 51 * Each stream is a single overlay. In Xv these are called ports. 52 */ 53 struct mutex mutex; 54 struct vmw_stream stream[VMW_MAX_NUM_STREAMS]; 55}; 56 57static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev) 58{ 59 struct vmw_private *dev_priv = vmw_priv(dev); 60 return dev_priv ? dev_priv->overlay_priv : NULL; 61} 62 63struct vmw_escape_header { 64 uint32_t cmd; 65 SVGAFifoCmdEscape body; 66}; 67 68struct vmw_escape_video_flush { 69 struct vmw_escape_header escape; 70 SVGAEscapeVideoFlush flush; 71}; 72 73static inline void fill_escape(struct vmw_escape_header *header, 74 uint32_t size) 75{ 76 header->cmd = SVGA_CMD_ESCAPE; 77 header->body.nsid = SVGA_ESCAPE_NSID_VMWARE; 78 header->body.size = size; 79} 80 81static inline void fill_flush(struct vmw_escape_video_flush *cmd, 82 uint32_t stream_id) 83{ 84 fill_escape(&cmd->escape, sizeof(cmd->flush)); 85 cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH; 86 cmd->flush.streamId = stream_id; 87} 88 89/** 90 * Pin or unpin a buffer in vram. 91 * 92 * @dev_priv: Driver private. 93 * @buf: DMA buffer to pin or unpin. 94 * @pin: Pin buffer in vram if true. 95 * @interruptible: Use interruptible wait. 96 * 97 * Takes the current masters ttm lock in read. 98 * 99 * Returns 100 * -ERESTARTSYS if interrupted by a signal. 101 */ 102static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv, 103 struct vmw_dma_buffer *buf, 104 bool pin, bool interruptible) 105{ 106 struct ttm_buffer_object *bo = &buf->base; 107 struct ttm_placement *overlay_placement = &vmw_vram_placement; 108 int ret; 109 110 ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible); 111 if (unlikely(ret != 0)) 112 return ret; 113 114 ret = ttm_bo_reserve(bo, interruptible, false, false, 0); 115 if (unlikely(ret != 0)) 116 goto err; 117 118 if (pin) 119 overlay_placement = &vmw_vram_ne_placement; 120 121 ret = ttm_bo_validate(bo, overlay_placement, interruptible, false, false); 122 123 ttm_bo_unreserve(bo); 124 125err: 126 ttm_read_unlock(&dev_priv->active_master->lock); 127 128 return ret; 129} 130 131/** 132 * Send put command to hw. 133 * 134 * Returns 135 * -ERESTARTSYS if interrupted by a signal. 136 */ 137static int vmw_overlay_send_put(struct vmw_private *dev_priv, 138 struct vmw_dma_buffer *buf, 139 struct drm_vmw_control_stream_arg *arg, 140 bool interruptible) 141{ 142 struct { 143 struct vmw_escape_header escape; 144 struct { 145 struct { 146 uint32_t cmdType; 147 uint32_t streamId; 148 } header; 149 struct { 150 uint32_t registerId; 151 uint32_t value; 152 } items[SVGA_VIDEO_PITCH_3 + 1]; 153 } body; 154 struct vmw_escape_video_flush flush; 155 } *cmds; 156 uint32_t offset; 157 int i, ret; 158 159 for (;;) { 160 cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds)); 161 if (cmds) 162 break; 163 164 ret = vmw_fallback_wait(dev_priv, false, true, 0, 165 interruptible, 3*HZ); 166 if (interruptible && ret == -ERESTARTSYS) 167 return ret; 168 else 169 BUG_ON(ret != 0); 170 } 171 172 fill_escape(&cmds->escape, sizeof(cmds->body)); 173 cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 174 cmds->body.header.streamId = arg->stream_id; 175 176 for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++) 177 cmds->body.items[i].registerId = i; 178 179 offset = buf->base.offset + arg->offset; 180 181 cmds->body.items[SVGA_VIDEO_ENABLED].value = true; 182 cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags; 183 cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset; 184 cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format; 185 cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key; 186 cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size; 187 cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width; 188 cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height; 189 cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x; 190 cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y; 191 cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; 192 cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; 193 cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x; 194 cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y; 195 cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; 196 cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; 197 cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; 198 cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; 199 cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; 200 201 fill_flush(&cmds->flush, arg->stream_id); 202 203 vmw_fifo_commit(dev_priv, sizeof(*cmds)); 204 205 return 0; 206} 207 208/** 209 * Send stop command to hw. 210 * 211 * Returns 212 * -ERESTARTSYS if interrupted by a signal. 213 */ 214static int vmw_overlay_send_stop(struct vmw_private *dev_priv, 215 uint32_t stream_id, 216 bool interruptible) 217{ 218 struct { 219 struct vmw_escape_header escape; 220 SVGAEscapeVideoSetRegs body; 221 struct vmw_escape_video_flush flush; 222 } *cmds; 223 int ret; 224 225 for (;;) { 226 cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds)); 227 if (cmds) 228 break; 229 230 ret = vmw_fallback_wait(dev_priv, false, true, 0, 231 interruptible, 3*HZ); 232 if (interruptible && ret == -ERESTARTSYS) 233 return ret; 234 else 235 BUG_ON(ret != 0); 236 } 237 238 fill_escape(&cmds->escape, sizeof(cmds->body)); 239 cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; 240 cmds->body.header.streamId = stream_id; 241 cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED; 242 cmds->body.items[0].value = false; 243 fill_flush(&cmds->flush, stream_id); 244 245 vmw_fifo_commit(dev_priv, sizeof(*cmds)); 246 247 return 0; 248} 249 250/** 251 * Stop or pause a stream. 252 * 253 * If the stream is paused the no evict flag is removed from the buffer 254 * but left in vram. This allows for instance mode_set to evict it 255 * should it need to. 256 * 257 * The caller must hold the overlay lock. 258 * 259 * @stream_id which stream to stop/pause. 260 * @pause true to pause, false to stop completely. 261 */ 262static int vmw_overlay_stop(struct vmw_private *dev_priv, 263 uint32_t stream_id, bool pause, 264 bool interruptible) 265{ 266 struct vmw_overlay *overlay = dev_priv->overlay_priv; 267 struct vmw_stream *stream = &overlay->stream[stream_id]; 268 int ret; 269 270 /* no buffer attached the stream is completely stopped */ 271 if (!stream->buf) 272 return 0; 273 274 /* If the stream is paused this is already done */ 275 if (!stream->paused) { 276 ret = vmw_overlay_send_stop(dev_priv, stream_id, 277 interruptible); 278 if (ret) 279 return ret; 280 281 /* We just remove the NO_EVICT flag so no -ENOMEM */ 282 ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false, 283 interruptible); 284 if (interruptible && ret == -ERESTARTSYS) 285 return ret; 286 else 287 BUG_ON(ret != 0); 288 } 289 290 if (!pause) { 291 vmw_dmabuf_unreference(&stream->buf); 292 stream->paused = false; 293 } else { 294 stream->paused = true; 295 } 296 297 return 0; 298} 299 300/** 301 * Update a stream and send any put or stop fifo commands needed. 302 * 303 * The caller must hold the overlay lock. 304 * 305 * Returns 306 * -ENOMEM if buffer doesn't fit in vram. 307 * -ERESTARTSYS if interrupted. 308 */ 309static int vmw_overlay_update_stream(struct vmw_private *dev_priv, 310 struct vmw_dma_buffer *buf, 311 struct drm_vmw_control_stream_arg *arg, 312 bool interruptible) 313{ 314 struct vmw_overlay *overlay = dev_priv->overlay_priv; 315 struct vmw_stream *stream = &overlay->stream[arg->stream_id]; 316 int ret = 0; 317 318 if (!buf) 319 return -EINVAL; 320 321 DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__, 322 stream->buf, buf, stream->paused ? "" : "not "); 323 324 if (stream->buf != buf) { 325 ret = vmw_overlay_stop(dev_priv, arg->stream_id, 326 false, interruptible); 327 if (ret) 328 return ret; 329 } else if (!stream->paused) { 330 /* If the buffers match and not paused then just send 331 * the put command, no need to do anything else. 332 */ 333 ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 334 if (ret == 0) 335 stream->saved = *arg; 336 else 337 BUG_ON(!interruptible); 338 339 return ret; 340 } 341 342 /* We don't start the old stream if we are interrupted. 343 * Might return -ENOMEM if it can't fit the buffer in vram. 344 */ 345 ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible); 346 if (ret) 347 return ret; 348 349 ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); 350 if (ret) { 351 /* This one needs to happen no matter what. We only remove 352 * the NO_EVICT flag so this is safe from -ENOMEM. 353 */ 354 BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0); 355 return ret; 356 } 357 358 if (stream->buf != buf) 359 stream->buf = vmw_dmabuf_reference(buf); 360 stream->saved = *arg; 361 362 return 0; 363} 364 365/** 366 * Stop all streams. 367 * 368 * Used by the fb code when starting. 369 * 370 * Takes the overlay lock. 371 */ 372int vmw_overlay_stop_all(struct vmw_private *dev_priv) 373{ 374 struct vmw_overlay *overlay = dev_priv->overlay_priv; 375 int i, ret; 376 377 if (!overlay) 378 return 0; 379 380 mutex_lock(&overlay->mutex); 381 382 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 383 struct vmw_stream *stream = &overlay->stream[i]; 384 if (!stream->buf) 385 continue; 386 387 ret = vmw_overlay_stop(dev_priv, i, false, false); 388 WARN_ON(ret != 0); 389 } 390 391 mutex_unlock(&overlay->mutex); 392 393 return 0; 394} 395 396/** 397 * Try to resume all paused streams. 398 * 399 * Used by the kms code after moving a new scanout buffer to vram. 400 * 401 * Takes the overlay lock. 402 */ 403int vmw_overlay_resume_all(struct vmw_private *dev_priv) 404{ 405 struct vmw_overlay *overlay = dev_priv->overlay_priv; 406 int i, ret; 407 408 if (!overlay) 409 return 0; 410 411 mutex_lock(&overlay->mutex); 412 413 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 414 struct vmw_stream *stream = &overlay->stream[i]; 415 if (!stream->paused) 416 continue; 417 418 ret = vmw_overlay_update_stream(dev_priv, stream->buf, 419 &stream->saved, false); 420 if (ret != 0) 421 DRM_INFO("%s: *warning* failed to resume stream %i\n", 422 __func__, i); 423 } 424 425 mutex_unlock(&overlay->mutex); 426 427 return 0; 428} 429 430/** 431 * Pauses all active streams. 432 * 433 * Used by the kms code when moving a new scanout buffer to vram. 434 * 435 * Takes the overlay lock. 436 */ 437int vmw_overlay_pause_all(struct vmw_private *dev_priv) 438{ 439 struct vmw_overlay *overlay = dev_priv->overlay_priv; 440 int i, ret; 441 442 if (!overlay) 443 return 0; 444 445 mutex_lock(&overlay->mutex); 446 447 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 448 if (overlay->stream[i].paused) 449 DRM_INFO("%s: *warning* stream %i already paused\n", 450 __func__, i); 451 ret = vmw_overlay_stop(dev_priv, i, true, false); 452 WARN_ON(ret != 0); 453 } 454 455 mutex_unlock(&overlay->mutex); 456 457 return 0; 458} 459 460int vmw_overlay_ioctl(struct drm_device *dev, void *data, 461 struct drm_file *file_priv) 462{ 463 struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; 464 struct vmw_private *dev_priv = vmw_priv(dev); 465 struct vmw_overlay *overlay = dev_priv->overlay_priv; 466 struct drm_vmw_control_stream_arg *arg = 467 (struct drm_vmw_control_stream_arg *)data; 468 struct vmw_dma_buffer *buf; 469 struct vmw_resource *res; 470 int ret; 471 472 if (!overlay) 473 return -ENOSYS; 474 475 ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); 476 if (ret) 477 return ret; 478 479 mutex_lock(&overlay->mutex); 480 481 if (!arg->enabled) { 482 ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true); 483 goto out_unlock; 484 } 485 486 ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf); 487 if (ret) 488 goto out_unlock; 489 490 ret = vmw_overlay_update_stream(dev_priv, buf, arg, true); 491 492 vmw_dmabuf_unreference(&buf); 493 494out_unlock: 495 mutex_unlock(&overlay->mutex); 496 vmw_resource_unreference(&res); 497 498 return ret; 499} 500 501int vmw_overlay_num_overlays(struct vmw_private *dev_priv) 502{ 503 if (!dev_priv->overlay_priv) 504 return 0; 505 506 return VMW_MAX_NUM_STREAMS; 507} 508 509int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv) 510{ 511 struct vmw_overlay *overlay = dev_priv->overlay_priv; 512 int i, k; 513 514 if (!overlay) 515 return 0; 516 517 mutex_lock(&overlay->mutex); 518 519 for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++) 520 if (!overlay->stream[i].claimed) 521 k++; 522 523 mutex_unlock(&overlay->mutex); 524 525 return k; 526} 527 528int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out) 529{ 530 struct vmw_overlay *overlay = dev_priv->overlay_priv; 531 int i; 532 533 if (!overlay) 534 return -ENOSYS; 535 536 mutex_lock(&overlay->mutex); 537 538 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 539 540 if (overlay->stream[i].claimed) 541 continue; 542 543 overlay->stream[i].claimed = true; 544 *out = i; 545 mutex_unlock(&overlay->mutex); 546 return 0; 547 } 548 549 mutex_unlock(&overlay->mutex); 550 return -ESRCH; 551} 552 553int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id) 554{ 555 struct vmw_overlay *overlay = dev_priv->overlay_priv; 556 557 BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS); 558 559 if (!overlay) 560 return -ENOSYS; 561 562 mutex_lock(&overlay->mutex); 563 564 WARN_ON(!overlay->stream[stream_id].claimed); 565 vmw_overlay_stop(dev_priv, stream_id, false, false); 566 overlay->stream[stream_id].claimed = false; 567 568 mutex_unlock(&overlay->mutex); 569 return 0; 570} 571 572int vmw_overlay_init(struct vmw_private *dev_priv) 573{ 574 struct vmw_overlay *overlay; 575 int i; 576 577 if (dev_priv->overlay_priv) 578 return -EINVAL; 579 580 if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_VIDEO) && 581 (dev_priv->fifo.capabilities & SVGA_FIFO_CAP_ESCAPE)) { 582 DRM_INFO("hardware doesn't support overlays\n"); 583 return -ENOSYS; 584 } 585 586 overlay = kmalloc(GFP_KERNEL, sizeof(*overlay)); 587 if (!overlay) 588 return -ENOMEM; 589 590 memset(overlay, 0, sizeof(*overlay)); 591 mutex_init(&overlay->mutex); 592 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 593 overlay->stream[i].buf = NULL; 594 overlay->stream[i].paused = false; 595 overlay->stream[i].claimed = false; 596 } 597 598 dev_priv->overlay_priv = overlay; 599 600 return 0; 601} 602 603int vmw_overlay_close(struct vmw_private *dev_priv) 604{ 605 struct vmw_overlay *overlay = dev_priv->overlay_priv; 606 bool forgotten_buffer = false; 607 int i; 608 609 if (!overlay) 610 return -ENOSYS; 611 612 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { 613 if (overlay->stream[i].buf) { 614 forgotten_buffer = true; 615 vmw_overlay_stop(dev_priv, i, false, false); 616 } 617 } 618 619 WARN_ON(forgotten_buffer); 620 621 dev_priv->overlay_priv = NULL; 622 kfree(overlay); 623 624 return 0; 625}