|
@@ -41,7 +41,7 @@ static void amdgpu_flip_callback(struct fence *f, struct fence_cb *cb)
|
|
container_of(cb, struct amdgpu_flip_work, cb);
|
|
container_of(cb, struct amdgpu_flip_work, cb);
|
|
|
|
|
|
fence_put(f);
|
|
fence_put(f);
|
|
- schedule_work(&work->flip_work);
|
|
|
|
|
|
+ schedule_work(&work->flip_work.work);
|
|
}
|
|
}
|
|
|
|
|
|
static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
|
|
static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
|
|
@@ -63,16 +63,17 @@ static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
|
|
|
|
|
|
static void amdgpu_flip_work_func(struct work_struct *__work)
|
|
static void amdgpu_flip_work_func(struct work_struct *__work)
|
|
{
|
|
{
|
|
|
|
+ struct delayed_work *delayed_work =
|
|
|
|
+ container_of(__work, struct delayed_work, work);
|
|
struct amdgpu_flip_work *work =
|
|
struct amdgpu_flip_work *work =
|
|
- container_of(__work, struct amdgpu_flip_work, flip_work);
|
|
|
|
|
|
+ container_of(delayed_work, struct amdgpu_flip_work, flip_work);
|
|
struct amdgpu_device *adev = work->adev;
|
|
struct amdgpu_device *adev = work->adev;
|
|
struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id];
|
|
struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id];
|
|
|
|
|
|
struct drm_crtc *crtc = &amdgpuCrtc->base;
|
|
struct drm_crtc *crtc = &amdgpuCrtc->base;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- unsigned i, repcnt = 4;
|
|
|
|
- int vpos, hpos, stat, min_udelay = 0;
|
|
|
|
- struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
|
|
|
|
|
|
+ unsigned i;
|
|
|
|
+ int vpos, hpos;
|
|
|
|
|
|
if (amdgpu_flip_handle_fence(work, &work->excl))
|
|
if (amdgpu_flip_handle_fence(work, &work->excl))
|
|
return;
|
|
return;
|
|
@@ -81,55 +82,23 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
|
|
if (amdgpu_flip_handle_fence(work, &work->shared[i]))
|
|
if (amdgpu_flip_handle_fence(work, &work->shared[i]))
|
|
return;
|
|
return;
|
|
|
|
|
|
- /* We borrow the event spin lock for protecting flip_status */
|
|
|
|
- spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
|
|
-
|
|
|
|
- /* If this happens to execute within the "virtually extended" vblank
|
|
|
|
- * interval before the start of the real vblank interval then it needs
|
|
|
|
- * to delay programming the mmio flip until the real vblank is entered.
|
|
|
|
- * This prevents completing a flip too early due to the way we fudge
|
|
|
|
- * our vblank counter and vblank timestamps in order to work around the
|
|
|
|
- * problem that the hw fires vblank interrupts before actual start of
|
|
|
|
- * vblank (when line buffer refilling is done for a frame). It
|
|
|
|
- * complements the fudging logic in amdgpu_get_crtc_scanoutpos() for
|
|
|
|
- * timestamping and amdgpu_get_vblank_counter_kms() for vblank counts.
|
|
|
|
- *
|
|
|
|
- * In practice this won't execute very often unless on very fast
|
|
|
|
- * machines because the time window for this to happen is very small.
|
|
|
|
|
|
+ /* Wait until we're out of the vertical blank period before the one
|
|
|
|
+ * targeted by the flip
|
|
*/
|
|
*/
|
|
- while (amdgpuCrtc->enabled && --repcnt) {
|
|
|
|
- /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
|
|
|
|
- * start in hpos, and to the "fudged earlier" vblank start in
|
|
|
|
- * vpos.
|
|
|
|
- */
|
|
|
|
- stat = amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id,
|
|
|
|
- GET_DISTANCE_TO_VBLANKSTART,
|
|
|
|
- &vpos, &hpos, NULL, NULL,
|
|
|
|
- &crtc->hwmode);
|
|
|
|
-
|
|
|
|
- if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
|
|
|
|
- (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) ||
|
|
|
|
- !(vpos >= 0 && hpos <= 0))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- /* Sleep at least until estimated real start of hw vblank */
|
|
|
|
- min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
|
|
|
|
- if (min_udelay > vblank->framedur_ns / 2000) {
|
|
|
|
- /* Don't wait ridiculously long - something is wrong */
|
|
|
|
- repcnt = 0;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
|
|
|
- usleep_range(min_udelay, 2 * min_udelay);
|
|
|
|
- spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
|
|
|
|
+ if (amdgpuCrtc->enabled &&
|
|
|
|
+ (amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, 0,
|
|
|
|
+ &vpos, &hpos, NULL, NULL,
|
|
|
|
+ &crtc->hwmode)
|
|
|
|
+ & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
|
|
|
|
+ (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
|
|
|
|
+ (int)(work->target_vblank -
|
|
|
|
+ amdgpu_get_vblank_counter_kms(adev->ddev, amdgpuCrtc->crtc_id)) > 0) {
|
|
|
|
+ schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000));
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (!repcnt)
|
|
|
|
- DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, "
|
|
|
|
- "framedur %d, linedur %d, stat %d, vpos %d, "
|
|
|
|
- "hpos %d\n", work->crtc_id, min_udelay,
|
|
|
|
- vblank->framedur_ns / 1000,
|
|
|
|
- vblank->linedur_ns / 1000, stat, vpos, hpos);
|
|
|
|
|
|
+ /* We borrow the event spin lock for protecting flip_status */
|
|
|
|
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
|
|
|
|
/* Do the flip (mmio) */
|
|
/* Do the flip (mmio) */
|
|
adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async);
|
|
adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async);
|
|
@@ -169,10 +138,10 @@ static void amdgpu_unpin_work_func(struct work_struct *__work)
|
|
kfree(work);
|
|
kfree(work);
|
|
}
|
|
}
|
|
|
|
|
|
-int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
|
|
|
|
- struct drm_framebuffer *fb,
|
|
|
|
- struct drm_pending_vblank_event *event,
|
|
|
|
- uint32_t page_flip_flags)
|
|
|
|
|
|
+int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
|
|
|
|
+ struct drm_framebuffer *fb,
|
|
|
|
+ struct drm_pending_vblank_event *event,
|
|
|
|
+ uint32_t page_flip_flags, uint32_t target)
|
|
{
|
|
{
|
|
struct drm_device *dev = crtc->dev;
|
|
struct drm_device *dev = crtc->dev;
|
|
struct amdgpu_device *adev = dev->dev_private;
|
|
struct amdgpu_device *adev = dev->dev_private;
|
|
@@ -191,7 +160,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
|
|
if (work == NULL)
|
|
if (work == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
- INIT_WORK(&work->flip_work, amdgpu_flip_work_func);
|
|
|
|
|
|
+ INIT_DELAYED_WORK(&work->flip_work, amdgpu_flip_work_func);
|
|
INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func);
|
|
INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func);
|
|
|
|
|
|
work->event = event;
|
|
work->event = event;
|
|
@@ -237,12 +206,8 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
|
|
amdgpu_bo_unreserve(new_rbo);
|
|
amdgpu_bo_unreserve(new_rbo);
|
|
|
|
|
|
work->base = base;
|
|
work->base = base;
|
|
-
|
|
|
|
- r = drm_crtc_vblank_get(crtc);
|
|
|
|
- if (r) {
|
|
|
|
- DRM_ERROR("failed to get vblank before flip\n");
|
|
|
|
- goto pflip_cleanup;
|
|
|
|
- }
|
|
|
|
|
|
+ work->target_vblank = target - drm_crtc_vblank_count(crtc) +
|
|
|
|
+ amdgpu_get_vblank_counter_kms(dev, work->crtc_id);
|
|
|
|
|
|
/* we borrow the event spin lock for protecting flip_wrok */
|
|
/* we borrow the event spin lock for protecting flip_wrok */
|
|
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
@@ -250,7 +215,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
|
|
DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
|
|
DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
|
|
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
|
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
|
r = -EBUSY;
|
|
r = -EBUSY;
|
|
- goto vblank_cleanup;
|
|
|
|
|
|
+ goto pflip_cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING;
|
|
amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING;
|
|
@@ -262,12 +227,9 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
|
|
/* update crtc fb */
|
|
/* update crtc fb */
|
|
crtc->primary->fb = fb;
|
|
crtc->primary->fb = fb;
|
|
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
|
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
|
- amdgpu_flip_work_func(&work->flip_work);
|
|
|
|
|
|
+ amdgpu_flip_work_func(&work->flip_work.work);
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
-vblank_cleanup:
|
|
|
|
- drm_crtc_vblank_put(crtc);
|
|
|
|
-
|
|
|
|
pflip_cleanup:
|
|
pflip_cleanup:
|
|
if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) {
|
|
if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) {
|
|
DRM_ERROR("failed to reserve new rbo in error path\n");
|
|
DRM_ERROR("failed to reserve new rbo in error path\n");
|