diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2016-01-12 05:01:12 -0500 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2016-01-12 05:01:12 -0500 |
| commit | 1f16f116b01c110db20ab808562c8b8bc3ee3d6e (patch) | |
| tree | 44db563f64cf5f8d62af8f99a61e2b248c44ea3a /drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | |
| parent | 03724ac3d48f8f0e3caf1d30fa134f8fd96c94e2 (diff) | |
| parent | f9eccf24615672896dc13251410c3f2f33a14f95 (diff) | |
Merge branches 'clockevents/4.4-fixes' and 'clockevents/4.5-fixes' of http://git.linaro.org/people/daniel.lezcano/linux into timers/urgent
Pull in fixes from Daniel Lezcano:
- Fix the vt8500 timer leading to a system lock up when dealing with too
small delta (Roman Volkov)
- Select the CLKSRC_MMIO when the fsl_ftm_timer is enabled with COMPILE_TEST
(Daniel Lezcano)
- Prevent to compile timers using the 'iomem' API when the architecture has
not HAS_IOMEM set (Richard Weinberger)
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_display.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | 108 |
1 files changed, 79 insertions, 29 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index e173a5a02f0d..5580d3420c3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | |||
| @@ -73,6 +73,8 @@ static void amdgpu_flip_work_func(struct work_struct *__work) | |||
| 73 | struct drm_crtc *crtc = &amdgpuCrtc->base; | 73 | struct drm_crtc *crtc = &amdgpuCrtc->base; |
| 74 | unsigned long flags; | 74 | unsigned long flags; |
| 75 | unsigned i; | 75 | unsigned i; |
| 76 | int vpos, hpos, stat, min_udelay; | ||
| 77 | struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id]; | ||
| 76 | 78 | ||
| 77 | amdgpu_flip_wait_fence(adev, &work->excl); | 79 | amdgpu_flip_wait_fence(adev, &work->excl); |
| 78 | for (i = 0; i < work->shared_count; ++i) | 80 | for (i = 0; i < work->shared_count; ++i) |
| @@ -81,6 +83,41 @@ static void amdgpu_flip_work_func(struct work_struct *__work) | |||
| 81 | /* We borrow the event spin lock for protecting flip_status */ | 83 | /* We borrow the event spin lock for protecting flip_status */ |
| 82 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | 84 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| 83 | 85 | ||
| 86 | /* If this happens to execute within the "virtually extended" vblank | ||
| 87 | * interval before the start of the real vblank interval then it needs | ||
| 88 | * to delay programming the mmio flip until the real vblank is entered. | ||
| 89 | * This prevents completing a flip too early due to the way we fudge | ||
| 90 | * our vblank counter and vblank timestamps in order to work around the | ||
| 91 | * problem that the hw fires vblank interrupts before actual start of | ||
| 92 | * vblank (when line buffer refilling is done for a frame). It | ||
| 93 | * complements the fudging logic in amdgpu_get_crtc_scanoutpos() for | ||
| 94 | * timestamping and amdgpu_get_vblank_counter_kms() for vblank counts. | ||
| 95 | * | ||
| 96 | * In practice this won't execute very often unless on very fast | ||
| 97 | * machines because the time window for this to happen is very small. | ||
| 98 | */ | ||
| 99 | for (;;) { | ||
| 100 | /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank | ||
| 101 | * start in hpos, and to the "fudged earlier" vblank start in | ||
| 102 | * vpos. | ||
| 103 | */ | ||
| 104 | stat = amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, | ||
| 105 | GET_DISTANCE_TO_VBLANKSTART, | ||
| 106 | &vpos, &hpos, NULL, NULL, | ||
| 107 | &crtc->hwmode); | ||
| 108 | |||
| 109 | if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) != | ||
| 110 | (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) || | ||
| 111 | !(vpos >= 0 && hpos <= 0)) | ||
| 112 | break; | ||
| 113 | |||
| 114 | /* Sleep at least until estimated real start of hw vblank */ | ||
| 115 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | ||
| 116 | min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5); | ||
| 117 | usleep_range(min_udelay, 2 * min_udelay); | ||
| 118 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | ||
| 119 | }; | ||
| 120 | |||
| 84 | /* do the flip (mmio) */ | 121 | /* do the flip (mmio) */ |
| 85 | adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base); | 122 | adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base); |
| 86 | /* set the flip status */ | 123 | /* set the flip status */ |
| @@ -109,7 +146,7 @@ static void amdgpu_unpin_work_func(struct work_struct *__work) | |||
| 109 | } else | 146 | } else |
| 110 | DRM_ERROR("failed to reserve buffer after flip\n"); | 147 | DRM_ERROR("failed to reserve buffer after flip\n"); |
| 111 | 148 | ||
| 112 | drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); | 149 | amdgpu_bo_unref(&work->old_rbo); |
| 113 | kfree(work->shared); | 150 | kfree(work->shared); |
| 114 | kfree(work); | 151 | kfree(work); |
| 115 | } | 152 | } |
| @@ -148,8 +185,8 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc, | |||
| 148 | obj = old_amdgpu_fb->obj; | 185 | obj = old_amdgpu_fb->obj; |
| 149 | 186 | ||
| 150 | /* take a reference to the old object */ | 187 | /* take a reference to the old object */ |
| 151 | drm_gem_object_reference(obj); | ||
| 152 | work->old_rbo = gem_to_amdgpu_bo(obj); | 188 | work->old_rbo = gem_to_amdgpu_bo(obj); |
| 189 | amdgpu_bo_ref(work->old_rbo); | ||
| 153 | 190 | ||
| 154 | new_amdgpu_fb = to_amdgpu_framebuffer(fb); | 191 | new_amdgpu_fb = to_amdgpu_framebuffer(fb); |
| 155 | obj = new_amdgpu_fb->obj; | 192 | obj = new_amdgpu_fb->obj; |
| @@ -222,7 +259,7 @@ pflip_cleanup: | |||
| 222 | amdgpu_bo_unreserve(new_rbo); | 259 | amdgpu_bo_unreserve(new_rbo); |
| 223 | 260 | ||
| 224 | cleanup: | 261 | cleanup: |
| 225 | drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); | 262 | amdgpu_bo_unref(&work->old_rbo); |
| 226 | fence_put(work->excl); | 263 | fence_put(work->excl); |
| 227 | for (i = 0; i < work->shared_count; ++i) | 264 | for (i = 0; i < work->shared_count; ++i) |
| 228 | fence_put(work->shared[i]); | 265 | fence_put(work->shared[i]); |
| @@ -712,6 +749,15 @@ bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc, | |||
| 712 | * \param dev Device to query. | 749 | * \param dev Device to query. |
| 713 | * \param pipe Crtc to query. | 750 | * \param pipe Crtc to query. |
| 714 | * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). | 751 | * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0). |
| 752 | * For driver internal use only also supports these flags: | ||
| 753 | * | ||
| 754 | * USE_REAL_VBLANKSTART to use the real start of vblank instead | ||
| 755 | * of a fudged earlier start of vblank. | ||
| 756 | * | ||
| 757 | * GET_DISTANCE_TO_VBLANKSTART to return distance to the | ||
| 758 | * fudged earlier start of vblank in *vpos and the distance | ||
| 759 | * to true start of vblank in *hpos. | ||
| 760 | * | ||
| 715 | * \param *vpos Location where vertical scanout position should be stored. | 761 | * \param *vpos Location where vertical scanout position should be stored. |
| 716 | * \param *hpos Location where horizontal scanout position should go. | 762 | * \param *hpos Location where horizontal scanout position should go. |
| 717 | * \param *stime Target location for timestamp taken immediately before | 763 | * \param *stime Target location for timestamp taken immediately before |
| @@ -776,10 +822,40 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, | |||
| 776 | vbl_end = 0; | 822 | vbl_end = 0; |
| 777 | } | 823 | } |
| 778 | 824 | ||
| 825 | /* Called from driver internal vblank counter query code? */ | ||
| 826 | if (flags & GET_DISTANCE_TO_VBLANKSTART) { | ||
| 827 | /* Caller wants distance from real vbl_start in *hpos */ | ||
| 828 | *hpos = *vpos - vbl_start; | ||
| 829 | } | ||
| 830 | |||
| 831 | /* Fudge vblank to start a few scanlines earlier to handle the | ||
| 832 | * problem that vblank irqs fire a few scanlines before start | ||
| 833 | * of vblank. Some driver internal callers need the true vblank | ||
| 834 | * start to be used and signal this via the USE_REAL_VBLANKSTART flag. | ||
| 835 | * | ||
| 836 | * The cause of the "early" vblank irq is that the irq is triggered | ||
| 837 | * by the line buffer logic when the line buffer read position enters | ||
| 838 | * the vblank, whereas our crtc scanout position naturally lags the | ||
| 839 | * line buffer read position. | ||
| 840 | */ | ||
| 841 | if (!(flags & USE_REAL_VBLANKSTART)) | ||
| 842 | vbl_start -= adev->mode_info.crtcs[pipe]->lb_vblank_lead_lines; | ||
| 843 | |||
| 779 | /* Test scanout position against vblank region. */ | 844 | /* Test scanout position against vblank region. */ |
| 780 | if ((*vpos < vbl_start) && (*vpos >= vbl_end)) | 845 | if ((*vpos < vbl_start) && (*vpos >= vbl_end)) |
| 781 | in_vbl = false; | 846 | in_vbl = false; |
| 782 | 847 | ||
| 848 | /* In vblank? */ | ||
| 849 | if (in_vbl) | ||
| 850 | ret |= DRM_SCANOUTPOS_IN_VBLANK; | ||
| 851 | |||
| 852 | /* Called from driver internal vblank counter query code? */ | ||
| 853 | if (flags & GET_DISTANCE_TO_VBLANKSTART) { | ||
| 854 | /* Caller wants distance from fudged earlier vbl_start */ | ||
| 855 | *vpos -= vbl_start; | ||
| 856 | return ret; | ||
| 857 | } | ||
| 858 | |||
| 783 | /* Check if inside vblank area and apply corrective offsets: | 859 | /* Check if inside vblank area and apply corrective offsets: |
| 784 | * vpos will then be >=0 in video scanout area, but negative | 860 | * vpos will then be >=0 in video scanout area, but negative |
| 785 | * within vblank area, counting down the number of lines until | 861 | * within vblank area, counting down the number of lines until |
| @@ -795,32 +871,6 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, | |||
| 795 | /* Correct for shifted end of vbl at vbl_end. */ | 871 | /* Correct for shifted end of vbl at vbl_end. */ |
| 796 | *vpos = *vpos - vbl_end; | 872 | *vpos = *vpos - vbl_end; |
| 797 | 873 | ||
| 798 | /* In vblank? */ | ||
| 799 | if (in_vbl) | ||
| 800 | ret |= DRM_SCANOUTPOS_IN_VBLANK; | ||
| 801 | |||
| 802 | /* Is vpos outside nominal vblank area, but less than | ||
| 803 | * 1/100 of a frame height away from start of vblank? | ||
| 804 | * If so, assume this isn't a massively delayed vblank | ||
| 805 | * interrupt, but a vblank interrupt that fired a few | ||
| 806 | * microseconds before true start of vblank. Compensate | ||
| 807 | * by adding a full frame duration to the final timestamp. | ||
| 808 | * Happens, e.g., on ATI R500, R600. | ||
| 809 | * | ||
| 810 | * We only do this if DRM_CALLED_FROM_VBLIRQ. | ||
| 811 | */ | ||
| 812 | if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { | ||
| 813 | vbl_start = mode->crtc_vdisplay; | ||
| 814 | vtotal = mode->crtc_vtotal; | ||
| 815 | |||
| 816 | if (vbl_start - *vpos < vtotal / 100) { | ||
| 817 | *vpos -= vtotal; | ||
| 818 | |||
| 819 | /* Signal this correction as "applied". */ | ||
| 820 | ret |= 0x8; | ||
| 821 | } | ||
| 822 | } | ||
| 823 | |||
| 824 | return ret; | 874 | return ret; |
| 825 | } | 875 | } |
| 826 | 876 | ||
