diff options
author | Michel Dänzer <michel.daenzer@amd.com> | 2016-08-03 23:39:39 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2016-08-10 14:28:07 -0400 |
commit | b8fc75cfee43232e12673ddebb3539ea805105ea (patch) | |
tree | 6eccfc601f4f242f7e020ce08962c1797041e16c /drivers/gpu/drm/radeon/radeon_display.c | |
parent | 3fd4b751c5409f4b9bf67d12b26356406b2af94c (diff) |
drm/radeon: Provide page_flip_target hook
Now we can program a flip during a vertical blank period, if it's the
one targeted by the flip (or a later one). This allows simplifying
radeon_flip_work_func considerably.
Acked-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Michel Dänzer <michel.daenzer@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_display.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 89 |
1 files changed, 24 insertions, 65 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index c3206fb8f4cf..890171f08987 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c | |||
@@ -400,14 +400,13 @@ static void radeon_flip_work_func(struct work_struct *__work) | |||
400 | struct radeon_flip_work *work = | 400 | struct radeon_flip_work *work = |
401 | container_of(__work, struct radeon_flip_work, flip_work); | 401 | container_of(__work, struct radeon_flip_work, flip_work); |
402 | struct radeon_device *rdev = work->rdev; | 402 | struct radeon_device *rdev = work->rdev; |
403 | struct drm_device *dev = rdev->ddev; | ||
403 | struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id]; | 404 | struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id]; |
404 | 405 | ||
405 | struct drm_crtc *crtc = &radeon_crtc->base; | 406 | struct drm_crtc *crtc = &radeon_crtc->base; |
406 | unsigned long flags; | 407 | unsigned long flags; |
407 | int r; | 408 | int r; |
408 | int vpos, hpos, stat, min_udelay = 0; | 409 | int vpos, hpos; |
409 | unsigned repcnt = 4; | ||
410 | struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id]; | ||
411 | 410 | ||
412 | down_read(&rdev->exclusive_lock); | 411 | down_read(&rdev->exclusive_lock); |
413 | if (work->fence) { | 412 | if (work->fence) { |
@@ -438,59 +437,25 @@ static void radeon_flip_work_func(struct work_struct *__work) | |||
438 | work->fence = NULL; | 437 | work->fence = NULL; |
439 | } | 438 | } |
440 | 439 | ||
440 | /* Wait until we're out of the vertical blank period before the one | ||
441 | * targeted by the flip | ||
442 | */ | ||
443 | while (radeon_crtc->enabled && | ||
444 | (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0, | ||
445 | &vpos, &hpos, NULL, NULL, | ||
446 | &crtc->hwmode) | ||
447 | & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == | ||
448 | (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && | ||
449 | (int)(work->target_vblank - | ||
450 | dev->driver->get_vblank_counter(dev, work->crtc_id)) > 0) | ||
451 | usleep_range(1000, 2000); | ||
452 | |||
441 | /* We borrow the event spin lock for protecting flip_status */ | 453 | /* We borrow the event spin lock for protecting flip_status */ |
442 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | 454 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
443 | 455 | ||
444 | /* set the proper interrupt */ | 456 | /* set the proper interrupt */ |
445 | radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); | 457 | radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id); |
446 | 458 | ||
447 | /* If this happens to execute within the "virtually extended" vblank | ||
448 | * interval before the start of the real vblank interval then it needs | ||
449 | * to delay programming the mmio flip until the real vblank is entered. | ||
450 | * This prevents completing a flip too early due to the way we fudge | ||
451 | * our vblank counter and vblank timestamps in order to work around the | ||
452 | * problem that the hw fires vblank interrupts before actual start of | ||
453 | * vblank (when line buffer refilling is done for a frame). It | ||
454 | * complements the fudging logic in radeon_get_crtc_scanoutpos() for | ||
455 | * timestamping and radeon_get_vblank_counter_kms() for vblank counts. | ||
456 | * | ||
457 | * In practice this won't execute very often unless on very fast | ||
458 | * machines because the time window for this to happen is very small. | ||
459 | */ | ||
460 | while (radeon_crtc->enabled && --repcnt) { | ||
461 | /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank | ||
462 | * start in hpos, and to the "fudged earlier" vblank start in | ||
463 | * vpos. | ||
464 | */ | ||
465 | stat = radeon_get_crtc_scanoutpos(rdev->ddev, work->crtc_id, | ||
466 | GET_DISTANCE_TO_VBLANKSTART, | ||
467 | &vpos, &hpos, NULL, NULL, | ||
468 | &crtc->hwmode); | ||
469 | |||
470 | if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) != | ||
471 | (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) || | ||
472 | !(vpos >= 0 && hpos <= 0)) | ||
473 | break; | ||
474 | |||
475 | /* Sleep at least until estimated real start of hw vblank */ | ||
476 | min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5); | ||
477 | if (min_udelay > vblank->framedur_ns / 2000) { | ||
478 | /* Don't wait ridiculously long - something is wrong */ | ||
479 | repcnt = 0; | ||
480 | break; | ||
481 | } | ||
482 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | ||
483 | usleep_range(min_udelay, 2 * min_udelay); | ||
484 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | ||
485 | }; | ||
486 | |||
487 | if (!repcnt) | ||
488 | DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, " | ||
489 | "framedur %d, linedur %d, stat %d, vpos %d, " | ||
490 | "hpos %d\n", work->crtc_id, min_udelay, | ||
491 | vblank->framedur_ns / 1000, | ||
492 | vblank->linedur_ns / 1000, stat, vpos, hpos); | ||
493 | |||
494 | /* do the flip (mmio) */ | 459 | /* do the flip (mmio) */ |
495 | radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async); | 460 | radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async); |
496 | 461 | ||
@@ -499,10 +464,11 @@ static void radeon_flip_work_func(struct work_struct *__work) | |||
499 | up_read(&rdev->exclusive_lock); | 464 | up_read(&rdev->exclusive_lock); |
500 | } | 465 | } |
501 | 466 | ||
502 | static int radeon_crtc_page_flip(struct drm_crtc *crtc, | 467 | static int radeon_crtc_page_flip_target(struct drm_crtc *crtc, |
503 | struct drm_framebuffer *fb, | 468 | struct drm_framebuffer *fb, |
504 | struct drm_pending_vblank_event *event, | 469 | struct drm_pending_vblank_event *event, |
505 | uint32_t page_flip_flags) | 470 | uint32_t page_flip_flags, |
471 | uint32_t target) | ||
506 | { | 472 | { |
507 | struct drm_device *dev = crtc->dev; | 473 | struct drm_device *dev = crtc->dev; |
508 | struct radeon_device *rdev = dev->dev_private; | 474 | struct radeon_device *rdev = dev->dev_private; |
@@ -599,12 +565,8 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, | |||
599 | base &= ~7; | 565 | base &= ~7; |
600 | } | 566 | } |
601 | work->base = base; | 567 | work->base = base; |
602 | 568 | work->target_vblank = target - drm_crtc_vblank_count(crtc) + | |
603 | r = drm_crtc_vblank_get(crtc); | 569 | dev->driver->get_vblank_counter(dev, work->crtc_id); |
604 | if (r) { | ||
605 | DRM_ERROR("failed to get vblank before flip\n"); | ||
606 | goto pflip_cleanup; | ||
607 | } | ||
608 | 570 | ||
609 | /* We borrow the event spin lock for protecting flip_work */ | 571 | /* We borrow the event spin lock for protecting flip_work */ |
610 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | 572 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
@@ -613,7 +575,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, | |||
613 | DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); | 575 | DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); |
614 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | 576 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
615 | r = -EBUSY; | 577 | r = -EBUSY; |
616 | goto vblank_cleanup; | 578 | goto pflip_cleanup; |
617 | } | 579 | } |
618 | radeon_crtc->flip_status = RADEON_FLIP_PENDING; | 580 | radeon_crtc->flip_status = RADEON_FLIP_PENDING; |
619 | radeon_crtc->flip_work = work; | 581 | radeon_crtc->flip_work = work; |
@@ -626,9 +588,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, | |||
626 | queue_work(radeon_crtc->flip_queue, &work->flip_work); | 588 | queue_work(radeon_crtc->flip_queue, &work->flip_work); |
627 | return 0; | 589 | return 0; |
628 | 590 | ||
629 | vblank_cleanup: | ||
630 | drm_crtc_vblank_put(crtc); | ||
631 | |||
632 | pflip_cleanup: | 591 | pflip_cleanup: |
633 | if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { | 592 | if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) { |
634 | DRM_ERROR("failed to reserve new rbo in error path\n"); | 593 | DRM_ERROR("failed to reserve new rbo in error path\n"); |
@@ -697,7 +656,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = { | |||
697 | .gamma_set = radeon_crtc_gamma_set, | 656 | .gamma_set = radeon_crtc_gamma_set, |
698 | .set_config = radeon_crtc_set_config, | 657 | .set_config = radeon_crtc_set_config, |
699 | .destroy = radeon_crtc_destroy, | 658 | .destroy = radeon_crtc_destroy, |
700 | .page_flip = radeon_crtc_page_flip, | 659 | .page_flip_target = radeon_crtc_page_flip_target, |
701 | }; | 660 | }; |
702 | 661 | ||
703 | static void radeon_crtc_init(struct drm_device *dev, int index) | 662 | static void radeon_crtc_init(struct drm_device *dev, int index) |