drm/armada: convert page_flip to use primary plane atomic_update()
page_flip requests happen asynchronously, so we can't wait on the vblank event before returning to userspace, as the transitional plane update helper would do. Craft our own implementation that keeps the asynchronous behaviour of this request, while making use of the atomic infrastructure for the primary plane update. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This commit is contained in:
parent
c36045e17a
commit
de503ddff8
|
@ -11,6 +11,7 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <drm/drmP.h>
|
#include <drm/drmP.h>
|
||||||
|
#include <drm/drm_atomic.h>
|
||||||
#include <drm/drm_crtc_helper.h>
|
#include <drm/drm_crtc_helper.h>
|
||||||
#include <drm/drm_plane_helper.h>
|
#include <drm/drm_plane_helper.h>
|
||||||
#include <drm/drm_atomic_helper.h>
|
#include <drm/drm_atomic_helper.h>
|
||||||
|
@ -939,53 +940,81 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
|
||||||
* and a mode_set.
|
* and a mode_set.
|
||||||
*/
|
*/
|
||||||
static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
|
static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
|
||||||
struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags,
|
struct drm_framebuffer *fb, struct drm_pending_vblank_event *event,
|
||||||
struct drm_modeset_acquire_ctx *ctx)
|
uint32_t page_flip_flags, struct drm_modeset_acquire_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||||
|
struct drm_plane *plane = crtc->primary;
|
||||||
|
const struct drm_plane_helper_funcs *plane_funcs;
|
||||||
|
struct drm_plane_state *state;
|
||||||
struct armada_plane_work *work;
|
struct armada_plane_work *work;
|
||||||
unsigned i;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary);
|
/* Construct new state for the primary plane */
|
||||||
if (!work)
|
state = drm_atomic_helper_plane_duplicate_state(plane);
|
||||||
|
if (!state)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
work->event = event;
|
drm_atomic_set_fb_for_plane(state, fb);
|
||||||
work->old_fb = dcrtc->crtc.primary->fb;
|
|
||||||
|
|
||||||
i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs,
|
work = armada_drm_crtc_alloc_plane_work(plane);
|
||||||
dcrtc->interlaced);
|
if (!work) {
|
||||||
armada_reg_queue_end(work->regs, i);
|
ret = -ENOMEM;
|
||||||
|
goto put_state;
|
||||||
/*
|
|
||||||
* Ensure that we hold a reference on the new framebuffer.
|
|
||||||
* This has to match the behaviour in mode_set.
|
|
||||||
*/
|
|
||||||
drm_framebuffer_get(fb);
|
|
||||||
|
|
||||||
ret = armada_drm_plane_work_queue(dcrtc, work);
|
|
||||||
if (ret) {
|
|
||||||
/* Undo our reference above */
|
|
||||||
drm_framebuffer_put(fb);
|
|
||||||
kfree(work);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make sure we can get vblank interrupts */
|
||||||
|
ret = drm_crtc_vblank_get(crtc);
|
||||||
|
if (ret)
|
||||||
|
goto put_work;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are in transition to atomic modeset: update the atomic modeset
|
* If we have another work pending, we can't process this flip.
|
||||||
* state with the new framebuffer to keep the state consistent.
|
* The modeset locks protect us from another user queuing a work
|
||||||
|
* while we're setting up.
|
||||||
*/
|
*/
|
||||||
drm_framebuffer_assign(&dcrtc->crtc.primary->state->fb, fb);
|
if (drm_to_armada_plane(plane)->work) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto put_vblank;
|
||||||
|
}
|
||||||
|
|
||||||
|
work->event = event;
|
||||||
|
work->old_fb = plane->state->fb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hold a ref on the new fb while it's being displayed by the
|
||||||
|
* hardware. The old fb refcount will be released in the worker.
|
||||||
|
*/
|
||||||
|
drm_framebuffer_get(state->fb);
|
||||||
|
|
||||||
|
/* Point of no return */
|
||||||
|
swap(plane->state, state);
|
||||||
|
|
||||||
|
dcrtc->regs_idx = 0;
|
||||||
|
dcrtc->regs = work->regs;
|
||||||
|
|
||||||
|
plane_funcs = plane->helper_private;
|
||||||
|
plane_funcs->atomic_update(plane, state);
|
||||||
|
armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx);
|
||||||
|
|
||||||
|
/* Queue the work - this should never fail */
|
||||||
|
WARN_ON(armada_drm_plane_work_queue(dcrtc, work));
|
||||||
|
work = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finally, if the display is blanked, we won't receive an
|
* Finally, if the display is blanked, we won't receive an
|
||||||
* interrupt, so complete it now.
|
* interrupt, so complete it now.
|
||||||
*/
|
*/
|
||||||
if (dpms_blanked(dcrtc->dpms))
|
if (dpms_blanked(dcrtc->dpms))
|
||||||
armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary);
|
armada_drm_plane_work_run(dcrtc, plane);
|
||||||
|
|
||||||
return 0;
|
put_vblank:
|
||||||
|
drm_crtc_vblank_put(crtc);
|
||||||
|
put_work:
|
||||||
|
kfree(work);
|
||||||
|
put_state:
|
||||||
|
drm_atomic_helper_plane_destroy_state(plane, state);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
Loading…
Reference in New Issue