diff options
| author | Thomas Hellstrom <thellstrom@vmware.com> | 2016-02-12 02:54:07 -0500 |
|---|---|---|
| committer | Thomas Hellstrom <thellstrom@vmware.com> | 2016-03-14 09:56:44 -0400 |
| commit | b1097aeb6f23433c4e073a8d7447a8d919e1e163 (patch) | |
| tree | f05484cadab9eeddd1663ab2fa942297aec069e1 /drivers/gpu/drm/vmwgfx | |
| parent | 897b818077f3c11eda82ead57fd173c7f12f9796 (diff) | |
drm/vmwgfx: Rework screen target page flips v2
Gnome-Shell / Wayland assumes that page-flips can be done on a crtc
regardless of framebuffer size and the crtc position within the
framebuffer.
Therefore rework the screen target code to correctly handle changes in
framebuffer size and content_fb_type. Also make sure that we update
the screen target correctly when the content_fb_type is not
SAME_AS_DISPLAY.
This commit breaks out the framebuffer binding code from crtc_set so it
can be used both from page_flip() and crtc_set() and reworks those
functions a bit to be more robust.
v2: Address review comments by Sinclair Yeh.
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Sinclair Yeh <syeh@vmware.com>
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 422 |
1 files changed, 188 insertions, 234 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 4ef5ffd7189d..c93af718a740 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | |||
| @@ -96,7 +96,6 @@ struct vmw_stdu_surface_copy { | |||
| 96 | * content_vfbs dimensions, then this is a pointer into the | 96 | * content_vfbs dimensions, then this is a pointer into the |
| 97 | * corresponding field in content_vfbs. If not, then this | 97 | * corresponding field in content_vfbs. If not, then this |
| 98 | * is a separate buffer to which content_vfbs will blit to. | 98 | * is a separate buffer to which content_vfbs will blit to. |
| 99 | * @content_fb: holds the rendered content, can be a surface or DMA buffer | ||
| 100 | * @content_type: content_fb type | 99 | * @content_type: content_fb type |
| 101 | * @defined: true if the current display unit has been initialized | 100 | * @defined: true if the current display unit has been initialized |
| 102 | */ | 101 | */ |
| @@ -104,8 +103,6 @@ struct vmw_screen_target_display_unit { | |||
| 104 | struct vmw_display_unit base; | 103 | struct vmw_display_unit base; |
| 105 | 104 | ||
| 106 | struct vmw_surface *display_srf; | 105 | struct vmw_surface *display_srf; |
| 107 | struct drm_framebuffer *content_fb; | ||
| 108 | |||
| 109 | enum stdu_content_type content_fb_type; | 106 | enum stdu_content_type content_fb_type; |
| 110 | 107 | ||
| 111 | bool defined; | 108 | bool defined; |
| @@ -122,22 +119,6 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); | |||
| 122 | *****************************************************************************/ | 119 | *****************************************************************************/ |
| 123 | 120 | ||
| 124 | /** | 121 | /** |
| 125 | * vmw_stdu_pin_display - pins the resource associated with the display surface | ||
| 126 | * | ||
| 127 | * @stdu: contains the display surface | ||
| 128 | * | ||
| 129 | * Since the display surface can either be a private surface allocated by us, | ||
| 130 | * or it can point to the content surface, we use this function to not pin the | ||
| 131 | * same resource twice. | ||
| 132 | */ | ||
| 133 | static int vmw_stdu_pin_display(struct vmw_screen_target_display_unit *stdu) | ||
| 134 | { | ||
| 135 | return vmw_resource_pin(&stdu->display_srf->res, false); | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | /** | ||
| 141 | * vmw_stdu_unpin_display - unpins the resource associated with display surface | 122 | * vmw_stdu_unpin_display - unpins the resource associated with display surface |
| 142 | * | 123 | * |
| 143 | * @stdu: contains the display surface | 124 | * @stdu: contains the display surface |
| @@ -153,13 +134,7 @@ static void vmw_stdu_unpin_display(struct vmw_screen_target_display_unit *stdu) | |||
| 153 | struct vmw_resource *res = &stdu->display_srf->res; | 134 | struct vmw_resource *res = &stdu->display_srf->res; |
| 154 | 135 | ||
| 155 | vmw_resource_unpin(res); | 136 | vmw_resource_unpin(res); |
| 156 | 137 | vmw_surface_unreference(&stdu->display_srf); | |
| 157 | if (stdu->content_fb_type != SAME_AS_DISPLAY) { | ||
| 158 | vmw_resource_unreference(&res); | ||
| 159 | stdu->content_fb_type = SAME_AS_DISPLAY; | ||
| 160 | } | ||
| 161 | |||
| 162 | stdu->display_srf = NULL; | ||
| 163 | } | 138 | } |
| 164 | } | 139 | } |
| 165 | 140 | ||
| @@ -185,6 +160,9 @@ static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc) | |||
| 185 | * | 160 | * |
| 186 | * @dev_priv: VMW DRM device | 161 | * @dev_priv: VMW DRM device |
| 187 | * @stdu: display unit to create a Screen Target for | 162 | * @stdu: display unit to create a Screen Target for |
| 163 | * @mode: The mode to set. | ||
| 164 | * @crtc_x: X coordinate of screen target relative to framebuffer origin. | ||
| 165 | * @crtc_y: Y coordinate of screen target relative to framebuffer origin. | ||
| 188 | * | 166 | * |
| 189 | * Creates a STDU that we can used later. This function is called whenever the | 167 | * Creates a STDU that we can used later. This function is called whenever the |
| 190 | * framebuffer size changes. | 168 | * framebuffer size changes. |
| @@ -193,7 +171,9 @@ static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc) | |||
| 193 | * 0 on success, error code on failure | 171 | * 0 on success, error code on failure |
| 194 | */ | 172 | */ |
| 195 | static int vmw_stdu_define_st(struct vmw_private *dev_priv, | 173 | static int vmw_stdu_define_st(struct vmw_private *dev_priv, |
| 196 | struct vmw_screen_target_display_unit *stdu) | 174 | struct vmw_screen_target_display_unit *stdu, |
| 175 | struct drm_display_mode *mode, | ||
| 176 | int crtc_x, int crtc_y) | ||
| 197 | { | 177 | { |
| 198 | struct { | 178 | struct { |
| 199 | SVGA3dCmdHeader header; | 179 | SVGA3dCmdHeader header; |
| @@ -211,14 +191,14 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, | |||
| 211 | cmd->header.size = sizeof(cmd->body); | 191 | cmd->header.size = sizeof(cmd->body); |
| 212 | 192 | ||
| 213 | cmd->body.stid = stdu->base.unit; | 193 | cmd->body.stid = stdu->base.unit; |
| 214 | cmd->body.width = stdu->display_srf->base_size.width; | 194 | cmd->body.width = mode->hdisplay; |
| 215 | cmd->body.height = stdu->display_srf->base_size.height; | 195 | cmd->body.height = mode->vdisplay; |
| 216 | cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0; | 196 | cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0; |
| 217 | cmd->body.dpi = 0; | 197 | cmd->body.dpi = 0; |
| 218 | cmd->body.xRoot = stdu->base.crtc.x; | 198 | if (stdu->base.is_implicit) { |
| 219 | cmd->body.yRoot = stdu->base.crtc.y; | 199 | cmd->body.xRoot = crtc_x; |
| 220 | 200 | cmd->body.yRoot = crtc_y; | |
| 221 | if (!stdu->base.is_implicit) { | 201 | } else { |
| 222 | cmd->body.xRoot = stdu->base.gui_x; | 202 | cmd->body.xRoot = stdu->base.gui_x; |
| 223 | cmd->body.yRoot = stdu->base.gui_y; | 203 | cmd->body.yRoot = stdu->base.gui_y; |
| 224 | } | 204 | } |
| @@ -392,126 +372,43 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, | |||
| 392 | return ret; | 372 | return ret; |
| 393 | } | 373 | } |
| 394 | 374 | ||
| 395 | |||
| 396 | |||
| 397 | /** | 375 | /** |
| 398 | * vmw_stdu_crtc_set_config - Sets a mode | 376 | * vmw_stdu_bind_fb - Bind an fb to a defined screen target |
| 399 | * | ||
| 400 | * @set: mode parameters | ||
| 401 | * | 377 | * |
| 402 | * This function is the device-specific portion of the DRM CRTC mode set. | 378 | * @dev_priv: Pointer to a device private struct. |
| 403 | * For the SVGA device, we do this by defining a Screen Target, binding a | 379 | * @crtc: The crtc holding the screen target. |
| 404 | * GB Surface to that target, and finally update the screen target. | 380 | * @mode: The mode currently used by the screen target. Must be non-NULL. |
| 381 | * @new_fb: The new framebuffer to bind. Must be non-NULL. | ||
| 405 | * | 382 | * |
| 406 | * RETURNS: | 383 | * RETURNS: |
| 407 | * 0 on success, error code otherwise | 384 | * 0 on success, error code on failure. |
| 408 | */ | 385 | */ |
| 409 | static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | 386 | static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, |
| 387 | struct drm_crtc *crtc, | ||
| 388 | struct drm_display_mode *mode, | ||
| 389 | struct drm_framebuffer *new_fb) | ||
| 410 | { | 390 | { |
| 411 | struct vmw_private *dev_priv; | 391 | struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); |
| 412 | struct vmw_screen_target_display_unit *stdu; | 392 | struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); |
| 413 | struct vmw_framebuffer *vfb; | 393 | struct vmw_surface *new_display_srf = NULL; |
| 394 | enum stdu_content_type new_content_type; | ||
| 414 | struct vmw_framebuffer_surface *new_vfbs; | 395 | struct vmw_framebuffer_surface *new_vfbs; |
| 415 | struct drm_display_mode *mode; | 396 | int ret; |
| 416 | struct drm_framebuffer *new_fb; | ||
| 417 | struct drm_crtc *crtc; | ||
| 418 | struct drm_encoder *encoder; | ||
| 419 | struct drm_connector *connector; | ||
| 420 | int ret; | ||
| 421 | |||
| 422 | |||
| 423 | if (!set || !set->crtc) | ||
| 424 | return -EINVAL; | ||
| 425 | |||
| 426 | crtc = set->crtc; | ||
| 427 | crtc->x = set->x; | ||
| 428 | crtc->y = set->y; | ||
| 429 | stdu = vmw_crtc_to_stdu(crtc); | ||
| 430 | mode = set->mode; | ||
| 431 | new_fb = set->fb; | ||
| 432 | dev_priv = vmw_priv(crtc->dev); | ||
| 433 | |||
| 434 | |||
| 435 | if (set->num_connectors > 1) { | ||
| 436 | DRM_ERROR("Too many connectors\n"); | ||
| 437 | return -EINVAL; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (set->num_connectors == 1 && | ||
| 441 | set->connectors[0] != &stdu->base.connector) { | ||
| 442 | DRM_ERROR("Connectors don't match %p %p\n", | ||
| 443 | set->connectors[0], &stdu->base.connector); | ||
| 444 | return -EINVAL; | ||
| 445 | } | ||
| 446 | |||
| 447 | |||
| 448 | /* Since they always map one to one these are safe */ | ||
| 449 | connector = &stdu->base.connector; | ||
| 450 | encoder = &stdu->base.encoder; | ||
| 451 | |||
| 452 | |||
| 453 | /* | ||
| 454 | * After this point the CRTC will be considered off unless a new fb | ||
| 455 | * is bound | ||
| 456 | */ | ||
| 457 | if (stdu->defined) { | ||
| 458 | /* Unbind current surface by binding an invalid one */ | ||
| 459 | ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); | ||
| 460 | if (unlikely(ret != 0)) | ||
| 461 | return ret; | ||
| 462 | |||
| 463 | /* Update Screen Target, display will now be blank */ | ||
| 464 | if (crtc->primary->fb) { | ||
| 465 | vmw_stdu_update_st(dev_priv, stdu); | ||
| 466 | if (unlikely(ret != 0)) | ||
| 467 | return ret; | ||
| 468 | } | ||
| 469 | |||
| 470 | crtc->primary->fb = NULL; | ||
| 471 | crtc->enabled = false; | ||
| 472 | encoder->crtc = NULL; | ||
| 473 | connector->encoder = NULL; | ||
| 474 | |||
| 475 | vmw_stdu_unpin_display(stdu); | ||
| 476 | stdu->content_fb = NULL; | ||
| 477 | stdu->content_fb_type = SAME_AS_DISPLAY; | ||
| 478 | |||
| 479 | ret = vmw_stdu_destroy_st(dev_priv, stdu); | ||
| 480 | /* The hardware is hung, give up */ | ||
| 481 | if (unlikely(ret != 0)) | ||
| 482 | return ret; | ||
| 483 | } | ||
| 484 | |||
| 485 | |||
| 486 | /* Any of these conditions means the caller wants CRTC off */ | ||
| 487 | if (set->num_connectors == 0 || !mode || !new_fb) | ||
| 488 | return 0; | ||
| 489 | |||
| 490 | |||
| 491 | if (set->x + mode->hdisplay > new_fb->width || | ||
| 492 | set->y + mode->vdisplay > new_fb->height) { | ||
| 493 | DRM_ERROR("Set outside of framebuffer\n"); | ||
| 494 | return -EINVAL; | ||
| 495 | } | ||
| 496 | 397 | ||
| 497 | stdu->content_fb = new_fb; | 398 | WARN_ON_ONCE(!stdu->defined); |
| 498 | vfb = vmw_framebuffer_to_vfb(stdu->content_fb); | ||
| 499 | 399 | ||
| 500 | if (vfb->dmabuf) | 400 | if (!vfb->dmabuf && new_fb->width == mode->hdisplay && |
| 501 | stdu->content_fb_type = SEPARATE_DMA; | 401 | new_fb->height == mode->vdisplay) |
| 402 | new_content_type = SAME_AS_DISPLAY; | ||
| 403 | else if (vfb->dmabuf) | ||
| 404 | new_content_type = SEPARATE_DMA; | ||
| 405 | else | ||
| 406 | new_content_type = SEPARATE_SURFACE; | ||
| 502 | 407 | ||
| 503 | /* | 408 | if (new_content_type != SAME_AS_DISPLAY && |
| 504 | * If the requested mode is different than the width and height | 409 | !stdu->display_srf) { |
| 505 | * of the FB or if the content buffer is a DMA buf, then allocate | ||
| 506 | * a display FB that matches the dimension of the mode | ||
| 507 | */ | ||
| 508 | if (mode->hdisplay != new_fb->width || | ||
| 509 | mode->vdisplay != new_fb->height || | ||
| 510 | stdu->content_fb_type != SAME_AS_DISPLAY) { | ||
| 511 | struct vmw_surface content_srf; | 410 | struct vmw_surface content_srf; |
| 512 | struct drm_vmw_size display_base_size = {0}; | 411 | struct drm_vmw_size display_base_size = {0}; |
| 513 | struct vmw_surface *display_srf; | ||
| 514 | |||
| 515 | 412 | ||
| 516 | display_base_size.width = mode->hdisplay; | 413 | display_base_size.width = mode->hdisplay; |
| 517 | display_base_size.height = mode->vdisplay; | 414 | display_base_size.height = mode->vdisplay; |
| @@ -521,7 +418,7 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | |||
| 521 | * If content buffer is a DMA buf, then we have to construct | 418 | * If content buffer is a DMA buf, then we have to construct |
| 522 | * surface info | 419 | * surface info |
| 523 | */ | 420 | */ |
| 524 | if (stdu->content_fb_type == SEPARATE_DMA) { | 421 | if (new_content_type == SEPARATE_DMA) { |
| 525 | 422 | ||
| 526 | switch (new_fb->bits_per_pixel) { | 423 | switch (new_fb->bits_per_pixel) { |
| 527 | case 32: | 424 | case 32: |
| @@ -538,17 +435,13 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | |||
| 538 | 435 | ||
| 539 | default: | 436 | default: |
| 540 | DRM_ERROR("Invalid format\n"); | 437 | DRM_ERROR("Invalid format\n"); |
| 541 | ret = -EINVAL; | 438 | return -EINVAL; |
| 542 | goto err_unref_content; | ||
| 543 | } | 439 | } |
| 544 | 440 | ||
| 545 | content_srf.flags = 0; | 441 | content_srf.flags = 0; |
| 546 | content_srf.mip_levels[0] = 1; | 442 | content_srf.mip_levels[0] = 1; |
| 547 | content_srf.multisample_count = 0; | 443 | content_srf.multisample_count = 0; |
| 548 | } else { | 444 | } else { |
| 549 | |||
| 550 | stdu->content_fb_type = SEPARATE_SURFACE; | ||
| 551 | |||
| 552 | new_vfbs = vmw_framebuffer_to_vfbs(new_fb); | 445 | new_vfbs = vmw_framebuffer_to_vfbs(new_fb); |
| 553 | content_srf = *new_vfbs->surface; | 446 | content_srf = *new_vfbs->surface; |
| 554 | } | 447 | } |
| @@ -563,26 +456,125 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | |||
| 563 | content_srf.multisample_count, | 456 | content_srf.multisample_count, |
| 564 | 0, | 457 | 0, |
| 565 | display_base_size, | 458 | display_base_size, |
| 566 | &display_srf); | 459 | &new_display_srf); |
| 567 | if (unlikely(ret != 0)) { | 460 | if (unlikely(ret != 0)) { |
| 568 | DRM_ERROR("Cannot allocate a display FB.\n"); | 461 | DRM_ERROR("Could not allocate screen target surface.\n"); |
| 569 | goto err_unref_content; | 462 | return ret; |
| 570 | } | 463 | } |
| 571 | 464 | } else if (new_content_type == SAME_AS_DISPLAY) { | |
| 572 | stdu->display_srf = display_srf; | ||
| 573 | } else { | ||
| 574 | new_vfbs = vmw_framebuffer_to_vfbs(new_fb); | 465 | new_vfbs = vmw_framebuffer_to_vfbs(new_fb); |
| 575 | stdu->display_srf = new_vfbs->surface; | 466 | new_display_srf = vmw_surface_reference(new_vfbs->surface); |
| 576 | } | 467 | } |
| 577 | 468 | ||
| 469 | if (new_display_srf) { | ||
| 470 | /* Pin new surface before flipping */ | ||
| 471 | ret = vmw_resource_pin(&new_display_srf->res, false); | ||
| 472 | if (ret) | ||
| 473 | goto out_srf_unref; | ||
| 474 | |||
| 475 | ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res); | ||
| 476 | if (ret) | ||
| 477 | goto out_srf_unpin; | ||
| 578 | 478 | ||
| 579 | ret = vmw_stdu_pin_display(stdu); | 479 | /* Unpin and unreference old surface */ |
| 580 | if (unlikely(ret != 0)) { | 480 | vmw_stdu_unpin_display(stdu); |
| 581 | stdu->display_srf = NULL; | 481 | |
| 582 | goto err_unref_content; | 482 | /* Transfer the reference */ |
| 483 | stdu->display_srf = new_display_srf; | ||
| 484 | new_display_srf = NULL; | ||
| 583 | } | 485 | } |
| 584 | 486 | ||
| 585 | vmw_svga_enable(dev_priv); | 487 | crtc->primary->fb = new_fb; |
| 488 | stdu->content_fb_type = new_content_type; | ||
| 489 | return 0; | ||
| 490 | |||
| 491 | out_srf_unpin: | ||
| 492 | vmw_resource_unpin(&new_display_srf->res); | ||
| 493 | out_srf_unref: | ||
| 494 | vmw_surface_unreference(&new_display_srf); | ||
| 495 | return ret; | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * vmw_stdu_crtc_set_config - Sets a mode | ||
| 500 | * | ||
| 501 | * @set: mode parameters | ||
| 502 | * | ||
| 503 | * This function is the device-specific portion of the DRM CRTC mode set. | ||
| 504 | * For the SVGA device, we do this by defining a Screen Target, binding a | ||
| 505 | * GB Surface to that target, and finally update the screen target. | ||
| 506 | * | ||
| 507 | * RETURNS: | ||
| 508 | * 0 on success, error code otherwise | ||
| 509 | */ | ||
| 510 | static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | ||
| 511 | { | ||
| 512 | struct vmw_private *dev_priv; | ||
| 513 | struct vmw_screen_target_display_unit *stdu; | ||
| 514 | struct drm_display_mode *mode; | ||
| 515 | struct drm_framebuffer *new_fb; | ||
| 516 | struct drm_crtc *crtc; | ||
| 517 | struct drm_encoder *encoder; | ||
| 518 | struct drm_connector *connector; | ||
| 519 | bool turning_off; | ||
| 520 | int ret; | ||
| 521 | |||
| 522 | |||
| 523 | if (!set || !set->crtc) | ||
| 524 | return -EINVAL; | ||
| 525 | |||
| 526 | crtc = set->crtc; | ||
| 527 | stdu = vmw_crtc_to_stdu(crtc); | ||
| 528 | mode = set->mode; | ||
| 529 | new_fb = set->fb; | ||
| 530 | dev_priv = vmw_priv(crtc->dev); | ||
| 531 | turning_off = set->num_connectors == 0 || !mode || !new_fb; | ||
| 532 | |||
| 533 | if (set->num_connectors > 1) { | ||
| 534 | DRM_ERROR("Too many connectors\n"); | ||
| 535 | return -EINVAL; | ||
| 536 | } | ||
| 537 | |||
| 538 | if (set->num_connectors == 1 && | ||
| 539 | set->connectors[0] != &stdu->base.connector) { | ||
| 540 | DRM_ERROR("Connectors don't match %p %p\n", | ||
| 541 | set->connectors[0], &stdu->base.connector); | ||
| 542 | return -EINVAL; | ||
| 543 | } | ||
| 544 | |||
| 545 | if (!turning_off && (set->x + mode->hdisplay > new_fb->width || | ||
| 546 | set->y + mode->vdisplay > new_fb->height)) { | ||
| 547 | DRM_ERROR("Set outside of framebuffer\n"); | ||
| 548 | return -EINVAL; | ||
| 549 | } | ||
| 550 | |||
| 551 | /* Since they always map one to one these are safe */ | ||
| 552 | connector = &stdu->base.connector; | ||
| 553 | encoder = &stdu->base.encoder; | ||
| 554 | |||
| 555 | if (stdu->defined) { | ||
| 556 | ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); | ||
| 557 | if (ret) | ||
| 558 | return ret; | ||
| 559 | |||
| 560 | vmw_stdu_unpin_display(stdu); | ||
| 561 | (void) vmw_stdu_update_st(dev_priv, stdu); | ||
| 562 | |||
| 563 | ret = vmw_stdu_destroy_st(dev_priv, stdu); | ||
| 564 | if (ret) | ||
| 565 | return ret; | ||
| 566 | |||
| 567 | crtc->primary->fb = NULL; | ||
| 568 | crtc->enabled = false; | ||
| 569 | encoder->crtc = NULL; | ||
| 570 | connector->encoder = NULL; | ||
| 571 | stdu->content_fb_type = SAME_AS_DISPLAY; | ||
| 572 | crtc->x = set->x; | ||
| 573 | crtc->y = set->y; | ||
| 574 | } | ||
| 575 | |||
| 576 | if (turning_off) | ||
| 577 | return 0; | ||
| 586 | 578 | ||
| 587 | /* | 579 | /* |
| 588 | * Steps to displaying a surface, assume surface is already | 580 | * Steps to displaying a surface, assume surface is already |
| @@ -592,35 +584,32 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) | |||
| 592 | * 3. update that screen target (this is done later by | 584 | * 3. update that screen target (this is done later by |
| 593 | * vmw_kms_stdu_do_surface_dirty_or_present) | 585 | * vmw_kms_stdu_do_surface_dirty_or_present) |
| 594 | */ | 586 | */ |
| 595 | ret = vmw_stdu_define_st(dev_priv, stdu); | 587 | /* |
| 596 | if (unlikely(ret != 0)) | 588 | * Note on error handling: We can't really restore the crtc to |
| 597 | goto err_unpin_display_and_content; | 589 | * it's original state on error, but we at least update the |
| 590 | * current state to what's submitted to hardware to enable | ||
| 591 | * future recovery. | ||
| 592 | */ | ||
| 593 | vmw_svga_enable(dev_priv); | ||
| 594 | ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y); | ||
| 595 | if (ret) | ||
| 596 | return ret; | ||
| 598 | 597 | ||
| 599 | ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); | 598 | crtc->x = set->x; |
| 600 | if (unlikely(ret != 0)) | 599 | crtc->y = set->y; |
| 601 | goto err_unpin_destroy_st; | 600 | crtc->mode = *mode; |
| 602 | 601 | ||
| 602 | ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb); | ||
| 603 | if (ret) | ||
| 604 | return ret; | ||
| 603 | 605 | ||
| 606 | crtc->enabled = true; | ||
| 604 | connector->encoder = encoder; | 607 | connector->encoder = encoder; |
| 605 | encoder->crtc = crtc; | 608 | encoder->crtc = crtc; |
| 606 | 609 | ||
| 607 | crtc->mode = *mode; | 610 | return 0; |
| 608 | crtc->primary->fb = new_fb; | ||
| 609 | crtc->enabled = true; | ||
| 610 | |||
| 611 | return ret; | ||
| 612 | |||
| 613 | err_unpin_destroy_st: | ||
| 614 | vmw_stdu_destroy_st(dev_priv, stdu); | ||
| 615 | err_unpin_display_and_content: | ||
| 616 | vmw_stdu_unpin_display(stdu); | ||
| 617 | err_unref_content: | ||
| 618 | stdu->content_fb = NULL; | ||
| 619 | return ret; | ||
| 620 | } | 611 | } |
| 621 | 612 | ||
| 622 | |||
| 623 | |||
| 624 | /** | 613 | /** |
| 625 | * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target | 614 | * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target |
| 626 | * | 615 | * |
| @@ -648,59 +637,31 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, | |||
| 648 | { | 637 | { |
| 649 | struct vmw_private *dev_priv = vmw_priv(crtc->dev); | 638 | struct vmw_private *dev_priv = vmw_priv(crtc->dev); |
| 650 | struct vmw_screen_target_display_unit *stdu; | 639 | struct vmw_screen_target_display_unit *stdu; |
| 640 | struct drm_vmw_rect vclips; | ||
| 641 | struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); | ||
| 651 | int ret; | 642 | int ret; |
| 652 | 643 | ||
| 653 | if (crtc == NULL) | ||
| 654 | return -EINVAL; | ||
| 655 | |||
| 656 | dev_priv = vmw_priv(crtc->dev); | 644 | dev_priv = vmw_priv(crtc->dev); |
| 657 | stdu = vmw_crtc_to_stdu(crtc); | 645 | stdu = vmw_crtc_to_stdu(crtc); |
| 658 | crtc->primary->fb = new_fb; | ||
| 659 | stdu->content_fb = new_fb; | ||
| 660 | |||
| 661 | if (stdu->display_srf) { | ||
| 662 | /* | ||
| 663 | * If the display surface is the same as the content surface | ||
| 664 | * then remove the reference | ||
| 665 | */ | ||
| 666 | if (stdu->content_fb_type == SAME_AS_DISPLAY) { | ||
| 667 | if (stdu->defined) { | ||
| 668 | /* Unbind the current surface */ | ||
| 669 | ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); | ||
| 670 | if (unlikely(ret != 0)) | ||
| 671 | goto err_out; | ||
| 672 | } | ||
| 673 | vmw_stdu_unpin_display(stdu); | ||
| 674 | stdu->display_srf = NULL; | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | |||
| 679 | if (!new_fb) { | ||
| 680 | /* Blanks the display */ | ||
| 681 | (void) vmw_stdu_update_st(dev_priv, stdu); | ||
| 682 | |||
| 683 | return 0; | ||
| 684 | } | ||
| 685 | 646 | ||
| 647 | if (!stdu->defined) | ||
| 648 | return -EINVAL; | ||
| 686 | 649 | ||
| 687 | if (stdu->content_fb_type == SAME_AS_DISPLAY) { | 650 | ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb); |
| 688 | stdu->display_srf = vmw_framebuffer_to_vfbs(new_fb)->surface; | 651 | if (ret) |
| 689 | ret = vmw_stdu_pin_display(stdu); | 652 | return ret; |
| 690 | if (ret) { | ||
| 691 | stdu->display_srf = NULL; | ||
| 692 | goto err_out; | ||
| 693 | } | ||
| 694 | |||
| 695 | /* Bind display surface */ | ||
| 696 | ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); | ||
| 697 | if (unlikely(ret != 0)) | ||
| 698 | goto err_unpin_display_and_content; | ||
| 699 | } | ||
| 700 | 653 | ||
| 701 | /* Update display surface: after this point everything is bound */ | 654 | vclips.x = crtc->x; |
| 702 | ret = vmw_stdu_update_st(dev_priv, stdu); | 655 | vclips.y = crtc->y; |
| 703 | if (unlikely(ret != 0)) | 656 | vclips.w = crtc->mode.hdisplay; |
| 657 | vclips.h = crtc->mode.vdisplay; | ||
| 658 | if (vfb->dmabuf) | ||
| 659 | ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips, | ||
| 660 | 1, 1, true, false); | ||
| 661 | else | ||
| 662 | ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips, | ||
| 663 | NULL, 0, 0, 1, 1, NULL); | ||
| 664 | if (ret) | ||
| 704 | return ret; | 665 | return ret; |
| 705 | 666 | ||
| 706 | if (event) { | 667 | if (event) { |
| @@ -721,14 +682,7 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, | |||
| 721 | vmw_fifo_flush(dev_priv, false); | 682 | vmw_fifo_flush(dev_priv, false); |
| 722 | } | 683 | } |
| 723 | 684 | ||
| 724 | return ret; | 685 | return 0; |
| 725 | |||
| 726 | err_unpin_display_and_content: | ||
| 727 | vmw_stdu_unpin_display(stdu); | ||
| 728 | err_out: | ||
| 729 | crtc->primary->fb = NULL; | ||
| 730 | stdu->content_fb = NULL; | ||
| 731 | return ret; | ||
| 732 | } | 686 | } |
| 733 | 687 | ||
| 734 | 688 | ||
