modesetting: Add vblank synchronization support when using Present.

modesetting hooked up vblank support for DRI2, but was missing support
for vblanks in Present.

This is mostly copy and pasted from Keith's code in the intel driver.

v2: Use ms_crtc_msc_to_kernel_msc in ms_present_queue_vblank to hook
    up the vblank_offset workaround for bogus MSC values (which the
    DRI2 code already did).

    Also simplify the ms_present_get_crtc function.  vblank.c already
    implements the functionality; we just need to convert types.

v3: Fix ms_flush_drm_events return code.  I'd copied code where 0 meant
    success into a function that returned a boolean, so the return code
    was always backwards.

    Also add DebugPresent calls in ms_present_vblank_{handler,abort}.

Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Jason Ekstrand <jason.ekstrand@intel.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
Kenneth Graunke 2014-12-11 13:37:14 -08:00 committed by Keith Packard
parent 3b5be33fc5
commit 09230a2d43
5 changed files with 256 additions and 0 deletions

View File

@ -50,6 +50,7 @@ modesetting_drv_la_SOURCES = \
drmmode_display.h \
dumb_bo.c \
dumb_bo.h \
present.c \
vblank.c \
$(NULL)

View File

@ -1128,6 +1128,11 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the DRI2 extension.\n");
}
if (!ms_present_screen_init(pScreen)) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Failed to initialize the Present extension.\n");
}
}
#endif

View File

@ -114,6 +114,10 @@ uint32_t ms_drm_queue_alloc(xf86CrtcPtr crtc,
ms_drm_handler_proc handler,
ms_drm_abort_proc abort);
void ms_drm_abort(ScrnInfoPtr scrn,
Bool (*match)(void *data, void *match_data),
void *match_data);
xf86CrtcPtr ms_dri2_crtc_covering_drawable(DrawablePtr pDraw);
xf86CrtcPtr ms_covering_crtc(ScrnInfoPtr scrn, BoxPtr box,
xf86CrtcPtr desired, BoxPtr crtc_box_ret);
@ -129,3 +133,5 @@ void ms_dri2_close_screen(ScreenPtr screen);
Bool ms_vblank_screen_init(ScreenPtr screen);
void ms_vblank_close_screen(ScreenPtr screen);
Bool ms_present_screen_init(ScreenPtr screen);

View File

@ -0,0 +1,226 @@
/*
* Copyright © 2014 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifdef HAVE_DIX_CONFIG_H
#include "dix-config.h"
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <xf86.h>
#include <xf86Crtc.h>
#include <xf86drm.h>
#include <xf86str.h>
#include <present.h>
#include "driver.h"
#if 0
#define DebugPresent(x) ErrorF x
#else
#define DebugPresent(x)
#endif
struct ms_present_vblank_event {
uint64_t event_id;
};
static RRCrtcPtr
ms_present_get_crtc(WindowPtr window)
{
xf86CrtcPtr xf86_crtc = ms_dri2_crtc_covering_drawable(&window->drawable);
return xf86_crtc ? xf86_crtc->randr_crtc : NULL;
}
static int
ms_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
{
xf86CrtcPtr xf86_crtc = crtc->devPrivate;
return ms_get_crtc_ust_msc(xf86_crtc, ust, msc);
}
/*
* Flush the DRM event queue when full; makes space for new events.
*/
static Bool
ms_flush_drm_events(ScreenPtr screen)
{
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
struct pollfd p = { .fd = ms->fd, .events = POLLIN };
int r;
do {
r = poll(&p, 1, 0);
} while (r == -1 && (errno == EINTR || errno == EAGAIN));
if (r <= 0)
return TRUE;
return drmHandleEvent(ms->fd, &ms->event_context) >= 0;
}
/*
* Called when the queued vblank event has occurred
*/
static void
ms_present_vblank_handler(uint64_t msc, uint64_t usec, void *data)
{
struct ms_present_vblank_event *event = data;
DebugPresent(("\t\tmh %lld msc %llu\n",
(long long) event->event_id, (long long) msc));
present_event_notify(event->event_id, usec, msc);
free(event);
}
/*
* Called when the queued vblank is aborted
*/
static void
ms_present_vblank_abort(void *data)
{
struct ms_present_vblank_event *event = data;
DebugPresent(("\t\tma %lld\n", (long long) event->event_id));
free(event);
}
/*
* Queue an event to report back to the Present extension when the specified
* MSC has past
*/
static int
ms_present_queue_vblank(RRCrtcPtr crtc,
uint64_t event_id,
uint64_t msc)
{
xf86CrtcPtr xf86_crtc = crtc->devPrivate;
ScreenPtr screen = crtc->pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
struct ms_present_vblank_event *event;
drmVBlank vbl;
int ret;
uint32_t seq;
event = calloc(sizeof(struct ms_present_vblank_event), 1);
if (!event)
return BadAlloc;
event->event_id = event_id;
seq = ms_drm_queue_alloc(xf86_crtc, event,
ms_present_vblank_handler,
ms_present_vblank_abort);
if (!seq) {
free(event);
return BadAlloc;
}
vbl.request.type =
DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
vbl.request.sequence = ms_crtc_msc_to_kernel_msc(xf86_crtc, msc);
vbl.request.signal = seq;
for (;;) {
ret = drmWaitVBlank(ms->fd, &vbl);
if (!ret)
break;
if (errno != EBUSY || !ms_flush_drm_events(screen))
return BadAlloc;
}
DebugPresent(("\t\tmq %lld seq %u msc %llu (hw msc %u)\n",
(long long) event_id, seq, (long long) msc,
vbl.request.sequence));
return Success;
}
static Bool
ms_present_event_match(void *data, void *match_data)
{
struct ms_present_vblank_event *event = data;
uint64_t *match = match_data;
return *match == event->event_id;
}
/*
* Remove a pending vblank event from the DRM queue so that it is not reported
* to the extension
*/
static void
ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
{
ScreenPtr screen = crtc->pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
ms_drm_abort(scrn, ms_present_event_match, &event_id);
}
/*
* Flush our batch buffer when requested by the Present extension.
*/
static void
ms_present_flush(WindowPtr window)
{
ScreenPtr screen = window->drawable.pScreen;
ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
modesettingPtr ms = modesettingPTR(scrn);
if (ms->drmmode.glamor)
glamor_block_handler(screen);
}
static present_screen_info_rec ms_present_screen_info = {
.version = PRESENT_SCREEN_INFO_VERSION,
.get_crtc = ms_present_get_crtc,
.get_ust_msc = ms_present_get_ust_msc,
.queue_vblank = ms_present_queue_vblank,
.abort_vblank = ms_present_abort_vblank,
.flush = ms_present_flush,
.capabilities = PresentCapabilityNone,
.check_flip = 0,
.flip = 0,
.unflip = 0,
};
Bool
ms_present_screen_init(ScreenPtr screen)
{
return present_screen_init(screen, &ms_present_screen_info);
}

View File

@ -330,6 +330,24 @@ ms_drm_abort_scrn(ScrnInfoPtr scrn)
}
}
/*
* Externally usable abort function that uses a callback to match a single
* queued entry to abort
*/
void
ms_drm_abort(ScrnInfoPtr scrn, Bool (*match)(void *data, void *match_data),
void *match_data)
{
struct ms_drm_queue *q;
xorg_list_for_each_entry(q, &ms_drm_queue, list) {
if (match(q->data, match_data)) {
ms_drm_abort_one(q);
break;
}
}
}
/*
* General DRM kernel handler. Looks for the matching sequence number in the
* drm event queue and calls the handler for it.