Commit 325cbba1 authored by Michel Dänzer's avatar Michel Dänzer Committed by Alex Deucher

drm/amdgpu: 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
amdgpu_flip_work_func considerably.

agd: update dce_virtual.c as well.
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarMichel Dänzer <michel.daenzer@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c229bfbb
...@@ -730,10 +730,11 @@ void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev, ...@@ -730,10 +730,11 @@ void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev,
*/ */
struct amdgpu_flip_work { struct amdgpu_flip_work {
struct work_struct flip_work; struct delayed_work flip_work;
struct work_struct unpin_work; struct work_struct unpin_work;
struct amdgpu_device *adev; struct amdgpu_device *adev;
int crtc_id; int crtc_id;
u32 target_vblank;
uint64_t base; uint64_t base;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
struct amdgpu_bo *old_rbo; struct amdgpu_bo *old_rbo;
......
...@@ -41,7 +41,7 @@ static void amdgpu_flip_callback(struct fence *f, struct fence_cb *cb) ...@@ -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, ...@@ -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; unsigned i;
int vpos, hpos, stat, min_udelay = 0; int vpos, hpos;
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
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) ...@@ -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 */ /* Wait until we're out of the vertical blank period before the one
spin_lock_irqsave(&crtc->dev->event_lock, flags); * targeted by the flip
/* 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.
*/ */
while (amdgpuCrtc->enabled && --repcnt) { if (amdgpuCrtc->enabled &&
/* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank (amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, 0,
* start in hpos, and to the "fudged earlier" vblank start in &vpos, &hpos, NULL, NULL,
* vpos. &crtc->hwmode)
*/ & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
stat = amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
GET_DISTANCE_TO_VBLANKSTART, (int)(work->target_vblank -
&vpos, &hpos, NULL, NULL, amdgpu_get_vblank_counter_kms(adev->ddev, amdgpuCrtc->crtc_id)) > 0) {
&crtc->hwmode); schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000));
return;
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 (!repcnt) /* We borrow the event spin lock for protecting flip_status */
DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, " spin_lock_irqsave(&crtc->dev->event_lock, flags);
"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);
/* 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) ...@@ -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, int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event, struct drm_pending_vblank_event *event,
uint32_t page_flip_flags) 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, ...@@ -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, ...@@ -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;
work->target_vblank = target - drm_crtc_vblank_count(crtc) +
r = drm_crtc_vblank_get(crtc); amdgpu_get_vblank_counter_kms(dev, work->crtc_id);
if (r) {
DRM_ERROR("failed to get vblank before flip\n");
goto pflip_cleanup;
}
/* 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, ...@@ -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, ...@@ -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");
......
...@@ -591,10 +591,10 @@ int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tile ...@@ -591,10 +591,10 @@ int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tile
void amdgpu_print_display_setup(struct drm_device *dev); void amdgpu_print_display_setup(struct drm_device *dev);
int amdgpu_modeset_create_props(struct amdgpu_device *adev); int amdgpu_modeset_create_props(struct amdgpu_device *adev);
int amdgpu_crtc_set_config(struct drm_mode_set *set); int amdgpu_crtc_set_config(struct drm_mode_set *set);
int amdgpu_crtc_page_flip(struct drm_crtc *crtc, int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event, struct drm_pending_vblank_event *event,
uint32_t page_flip_flags); uint32_t page_flip_flags, uint32_t target);
extern const struct drm_mode_config_funcs amdgpu_mode_funcs; extern const struct drm_mode_config_funcs amdgpu_mode_funcs;
#endif #endif
...@@ -2737,7 +2737,7 @@ static const struct drm_crtc_funcs dce_v10_0_crtc_funcs = { ...@@ -2737,7 +2737,7 @@ static const struct drm_crtc_funcs dce_v10_0_crtc_funcs = {
.gamma_set = dce_v10_0_crtc_gamma_set, .gamma_set = dce_v10_0_crtc_gamma_set,
.set_config = amdgpu_crtc_set_config, .set_config = amdgpu_crtc_set_config,
.destroy = dce_v10_0_crtc_destroy, .destroy = dce_v10_0_crtc_destroy,
.page_flip = amdgpu_crtc_page_flip, .page_flip_target = amdgpu_crtc_page_flip_target,
}; };
static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode) static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode)
......
...@@ -2755,7 +2755,7 @@ static const struct drm_crtc_funcs dce_v11_0_crtc_funcs = { ...@@ -2755,7 +2755,7 @@ static const struct drm_crtc_funcs dce_v11_0_crtc_funcs = {
.gamma_set = dce_v11_0_crtc_gamma_set, .gamma_set = dce_v11_0_crtc_gamma_set,
.set_config = amdgpu_crtc_set_config, .set_config = amdgpu_crtc_set_config,
.destroy = dce_v11_0_crtc_destroy, .destroy = dce_v11_0_crtc_destroy,
.page_flip = amdgpu_crtc_page_flip, .page_flip_target = amdgpu_crtc_page_flip_target,
}; };
static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode) static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode)
......
...@@ -2598,7 +2598,7 @@ static const struct drm_crtc_funcs dce_v8_0_crtc_funcs = { ...@@ -2598,7 +2598,7 @@ static const struct drm_crtc_funcs dce_v8_0_crtc_funcs = {
.gamma_set = dce_v8_0_crtc_gamma_set, .gamma_set = dce_v8_0_crtc_gamma_set,
.set_config = amdgpu_crtc_set_config, .set_config = amdgpu_crtc_set_config,
.destroy = dce_v8_0_crtc_destroy, .destroy = dce_v8_0_crtc_destroy,
.page_flip = amdgpu_crtc_page_flip, .page_flip_target = amdgpu_crtc_page_flip_target,
}; };
static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode) static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode)
......
...@@ -188,7 +188,7 @@ static const struct drm_crtc_funcs dce_virtual_crtc_funcs = { ...@@ -188,7 +188,7 @@ static const struct drm_crtc_funcs dce_virtual_crtc_funcs = {
.gamma_set = dce_virtual_crtc_gamma_set, .gamma_set = dce_virtual_crtc_gamma_set,
.set_config = amdgpu_crtc_set_config, .set_config = amdgpu_crtc_set_config,
.destroy = dce_virtual_crtc_destroy, .destroy = dce_virtual_crtc_destroy,
.page_flip = amdgpu_crtc_page_flip, .page_flip_target = amdgpu_crtc_page_flip_target,
}; };
static void dce_virtual_crtc_dpms(struct drm_crtc *crtc, int mode) static void dce_virtual_crtc_dpms(struct drm_crtc *crtc, int mode)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment