Commit 30591320 authored by Jerome Glisse's avatar Jerome Glisse
Browse files

kms: add support for the MSC swap & sync API



This patch is mostly a port over of Intel ddx code for
MSC support. It needs a radeon KMS module with version
2.4 which has the query for hw crtc id.
Signed-off-by: Jesse Barnes's avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarJerome Glisse <jglisse@redhat.com>
parent 766024dc
......@@ -522,6 +522,32 @@ static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
.destroy = NULL, /* XXX */
};
int drmmode_get_crtc_id(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
return drmmode_crtc->hw_id;
}
void drmmode_crtc_hw_id(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
ScrnInfoPtr pScrn = crtc->scrn;
RADEONInfoPtr info = RADEONPTR(pScrn);
struct drm_radeon_info ginfo;
int r;
uint32_t tmp;
memset(&ginfo, 0, sizeof(ginfo));
ginfo.request = 0x4;
tmp = drmmode_crtc->mode_crtc->crtc_id;
ginfo.value = (uintptr_t)&tmp;
r = drmCommandWriteRead(info->dri->drmFD, DRM_RADEON_INFO, &ginfo, sizeof(ginfo));
if (r) {
drmmode_crtc->hw_id = -1;
return;
}
drmmode_crtc->hw_id = tmp;
}
static void
drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
......@@ -537,6 +563,7 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]);
drmmode_crtc->drmmode = drmmode;
crtc->driver_private = drmmode_crtc;
drmmode_crtc_hw_id(crtc);
return;
}
......@@ -1150,10 +1177,28 @@ static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
drmmode_xf86crtc_resize
};
static void
drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec,
unsigned int tv_usec, void *event_data)
{
radeon_dri2_frame_event_handler(frame, tv_sec, tv_usec, event_data);
}
static void
drm_wakeup_handler(pointer data, int err, pointer p)
{
drmmode_ptr drmmode = data;
fd_set *read_mask = p;
if (err >= 0 && FD_ISSET(drmmode->fd, read_mask)) {
drmHandleEvent(drmmode->fd, &drmmode->event_context);
}
}
Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
{
xf86CrtcConfigPtr xf86_config;
xf86CrtcConfigPtr xf86_config;
RADEONInfoPtr info = RADEONPTR(pScrn);
int i;
xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
......@@ -1178,6 +1223,16 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
xf86InitialConfiguration(pScrn, TRUE);
drmmode->flip_count = 0;
drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION;
drmmode->event_context.vblank_handler = drmmode_vblank_handler;
drmmode->event_context.page_flip_handler = NULL;
if (info->dri->pKernelDRMVersion->version_minor >= 4) {
AddGeneralSocket(drmmode->fd);
RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
drm_wakeup_handler, drmmode);
}
return TRUE;
}
......
......@@ -48,11 +48,14 @@ typedef struct {
struct udev_monitor *uevent_monitor;
InputHandlerProc uevent_handler;
#endif
drmEventContext event_context;
int flip_count;
} drmmode_rec, *drmmode_ptr;
typedef struct {
drmmode_ptr drmmode;
drmModeCrtcPtr mode_crtc;
int hw_id;
struct radeon_bo *cursor_bo;
struct radeon_bo *rotate_bo;
unsigned rotate_fb_id;
......
......@@ -324,12 +324,450 @@ radeon_dri2_copy_region(DrawablePtr drawable,
radeon_cs_flush_indirect(pScrn);
}
#if DRI2INFOREC_VERSION >= 4
enum DRI2FrameEventType {
DRI2_SWAP,
DRI2_FLIP,
DRI2_WAITMSC,
};
typedef struct _DRI2FrameEvent {
XID drawable_id;
ClientPtr client;
enum DRI2FrameEventType type;
int frame;
/* for swaps & flips only */
DRI2SwapEventPtr event_complete;
void *event_data;
DRI2BufferPtr front;
DRI2BufferPtr back;
} DRI2FrameEventRec, *DRI2FrameEventPtr;
void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
unsigned int tv_usec, void *event_data)
{
DRI2FrameEventPtr event = event_data;
DrawablePtr drawable;
ScreenPtr screen;
ScrnInfoPtr scrn;
int status;
int swap_type;
BoxRec box;
RegionRec region;
status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
M_ANY, DixWriteAccess);
if (status != Success) {
xfree(event);
return;
}
screen = drawable->pScreen;
scrn = xf86Screens[screen->myNum];
switch (event->type) {
case DRI2_FLIP:
case DRI2_SWAP:
box.x1 = 0;
box.y1 = 0;
box.x2 = drawable->width;
box.y2 = drawable->height;
REGION_INIT(pScreen, &region, &box, 0);
radeon_dri2_copy_region(drawable, &region, event->front, event->back);
swap_type = DRI2_BLIT_COMPLETE;
DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec,
swap_type, event->event_complete, event->event_data);
break;
case DRI2_WAITMSC:
DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, tv_usec);
break;
default:
/* Unknown type */
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"%s: unknown vblank event received\n", __func__);
break;
}
xfree(event);
}
static int radeon_dri2_drawable_crtc(DrawablePtr pDraw)
{
ScreenPtr pScreen = pDraw->pScreen;
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
BoxRec box, crtcbox;
xf86CrtcPtr crtc;
int crtc_id = -1;
box.x1 = pDraw->x;
box.y1 = pDraw->y;
box.x2 = box.x1 + pDraw->width;
box.y2 = box.y1 + pDraw->height;
crtc = radeon_covering_crtc(pScrn, &box, NULL, &crtcbox);
/* Make sure the CRTC is valid and this is the real front buffer */
if (crtc != NULL && !crtc->rotatedData) {
crtc_id = drmmode_get_crtc_id(crtc);
}
return crtc_id;
}
/*
* Get current frame count and frame count timestamp, based on drawable's
* crtc.
*/
static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
{
ScreenPtr screen = draw->pScreen;
ScrnInfoPtr scrn = xf86Screens[screen->myNum];
RADEONInfoPtr info = RADEONPTR(scrn);
drmVBlank vbl;
int ret;
int crtc= radeon_dri2_drawable_crtc(draw);
/* Drawable not displayed, make up a value */
if (crtc == -1) {
*ust = 0;
*msc = 0;
return TRUE;
}
vbl.request.type = DRM_VBLANK_RELATIVE;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = 0;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
return FALSE;
}
*ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec;
*msc = vbl.reply.sequence;
return TRUE;
}
/*
* Request a DRM event when the requested conditions will be satisfied.
*
* We need to handle the event and ask the server to wake up the client when
* we receive it.
*/
static int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
CARD64 target_msc, CARD64 divisor,
CARD64 remainder)
{
ScreenPtr screen = draw->pScreen;
ScrnInfoPtr scrn = xf86Screens[screen->myNum];
RADEONInfoPtr info = RADEONPTR(scrn);
DRI2FrameEventPtr wait_info;
drmVBlank vbl;
int ret, crtc = radeon_dri2_drawable_crtc(draw);
CARD64 current_msc;
/* Truncate to match kernel interfaces; means occasional overflow
* misses, but that's generally not a big deal */
target_msc &= 0xffffffff;
divisor &= 0xffffffff;
remainder &= 0xffffffff;
/* Drawable not visible, return immediately */
if (crtc == -1)
goto out_complete;
wait_info = xcalloc(1, sizeof(DRI2FrameEventRec));
if (!wait_info)
goto out_complete;
wait_info->drawable_id = draw->id;
wait_info->client = client;
wait_info->type = DRI2_WAITMSC;
/* Get current count */
vbl.request.type = DRM_VBLANK_RELATIVE;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = 0;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
current_msc = vbl.reply.sequence;
/*
* If divisor is zero, or current_msc is smaller than target_msc,
* we just need to make sure target_msc passes before waking up the
* client.
*/
if (divisor == 0 || current_msc < target_msc) {
/* If target_msc already reached or passed, set it to
* current_msc to ensure we return a reasonable value back
* to the caller. This keeps the client from continually
* sending us MSC targets from the past by forcibly updating
* their count on this call.
*/
if (current_msc >= target_msc)
target_msc = current_msc;
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = target_msc;
vbl.request.signal = (unsigned long)wait_info;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
wait_info->frame = vbl.reply.sequence;
DRI2BlockClient(client, draw);
return TRUE;
}
/*
* If we get here, target_msc has already passed or we don't have one,
* so we queue an event that will satisfy the divisor/remainder equation.
*/
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = current_msc - (current_msc % divisor) +
remainder;
/*
* If calculated remainder is larger than requested remainder,
* it means we've passed the last point where
* seq % divisor == remainder, so we need to wait for the next time
* that will happen.
*/
if ((current_msc % divisor) >= remainder)
vbl.request.sequence += divisor;
vbl.request.signal = (unsigned long)wait_info;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
wait_info->frame = vbl.reply.sequence;
DRI2BlockClient(client, draw);
return TRUE;
out_complete:
DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
return TRUE;
}
/*
* ScheduleSwap is responsible for requesting a DRM vblank event for the
* appropriate frame.
*
* In the case of a blit (e.g. for a windowed swap) or buffer exchange,
* the vblank requested can simply be the last queued swap frame + the swap
* interval for the drawable.
*
* In the case of a page flip, we request an event for the last queued swap
* frame + swap interval - 1, since we'll need to queue the flip for the frame
* immediately following the received event.
*
* The client will be blocked if it tries to perform further GL commands
* after queueing a swap, though in the Intel case after queueing a flip, the
* client is free to queue more commands; they'll block in the kernel if
* they access buffers busy with the flip.
*
* When the swap is complete, the driver should call into the server so it
* can send any swap complete events that have been requested.
*/
static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
DRI2BufferPtr front, DRI2BufferPtr back,
CARD64 *target_msc, CARD64 divisor,
CARD64 remainder, DRI2SwapEventPtr func,
void *data)
{
ScreenPtr screen = draw->pScreen;
ScrnInfoPtr scrn = xf86Screens[screen->myNum];
RADEONInfoPtr info = RADEONPTR(scrn);
drmVBlank vbl;
int ret, crtc= radeon_dri2_drawable_crtc(draw), flip = 0;
DRI2FrameEventPtr swap_info;
enum DRI2FrameEventType swap_type = DRI2_SWAP;
CARD64 current_msc;
BoxRec box;
RegionRec region;
/* Truncate to match kernel interfaces; means occasional overflow
* misses, but that's generally not a big deal */
*target_msc &= 0xffffffff;
divisor &= 0xffffffff;
remainder &= 0xffffffff;
swap_info = xcalloc(1, sizeof(DRI2FrameEventRec));
/* Drawable not displayed... just complete the swap */
if (crtc == -1 || !swap_info)
goto blit_fallback;
swap_info->drawable_id = draw->id;
swap_info->client = client;
swap_info->event_complete = func;
swap_info->event_data = data;
swap_info->front = front;
swap_info->back = back;
/* Get current count */
vbl.request.type = DRM_VBLANK_RELATIVE;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = 0;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"first get vblank counter failed: %s\n",
strerror(errno));
goto blit_fallback;
}
current_msc = vbl.reply.sequence;
swap_info->type = swap_type;
/* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
* Do it early, so handling of different timing constraints
* for divisor, remainder and msc vs. target_msc works.
*/
if (*target_msc > 0)
*target_msc -= flip;
/*
* If divisor is zero, or current_msc is smaller than target_msc
* we just need to make sure target_msc passes before initiating
* the swap.
*/
if (divisor == 0 || current_msc < *target_msc) {
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
/* If non-pageflipping, but blitting/exchanging, we need to use
* DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
* on.
*/
if (flip == 0)
vbl.request.type |= DRM_VBLANK_NEXTONMISS;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
/* If target_msc already reached or passed, set it to
* current_msc to ensure we return a reasonable value back
* to the caller. This makes swap_interval logic more robust.
*/
if (current_msc >= *target_msc)
*target_msc = current_msc;
vbl.request.sequence = *target_msc;
vbl.request.signal = (unsigned long)swap_info;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"divisor 0 get vblank counter failed: %s\n",
strerror(errno));
goto blit_fallback;
}
*target_msc = vbl.reply.sequence + flip;
swap_info->frame = *target_msc;
return TRUE;
}
/*
* If we get here, target_msc has already passed or we don't have one,
* and we need to queue an event that will satisfy the divisor/remainder
* equation.
*/
vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
if (flip == 0)
vbl.request.type |= DRM_VBLANK_NEXTONMISS;
if (crtc > 0)
vbl.request.type |= DRM_VBLANK_SECONDARY;
vbl.request.sequence = current_msc - (current_msc % divisor) +
remainder;
/*
* If the calculated deadline vbl.request.sequence is smaller than
* or equal to current_msc, it means we've passed the last point
* when effective onset frame seq could satisfy
* seq % divisor == remainder, so we need to wait for the next time
* this will happen.
* This comparison takes the 1 frame swap delay in pageflipping mode
* into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
* if we are blitting/exchanging instead of flipping.
*/
if (vbl.request.sequence <= current_msc)
vbl.request.sequence += divisor;
/* Account for 1 frame extra pageflip delay if flip > 0 */
vbl.request.sequence -= flip;
vbl.request.signal = (unsigned long)swap_info;
ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"final get vblank counter failed: %s\n",
strerror(errno));
goto blit_fallback;
}
/* Adjust returned value for 1 fame pageflip offset of flip > 0 */
*target_msc = vbl.reply.sequence + flip;
swap_info->frame = *target_msc;
return TRUE;
blit_fallback:
box.x1 = 0;
box.y1 = 0;
box.x2 = draw->width;
box.y2 = draw->height;
REGION_INIT(pScreen, &region, &box, 0);
radeon_dri2_copy_region(draw, &region, front, back);
DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
if (swap_info)
xfree(swap_info);
*target_msc = 0; /* offscreen, so zero out target vblank count */
return TRUE;
}
#endif /* DRI2INFOREC_VERSION >= 4 */
Bool
radeon_dri2_screen_init(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
RADEONInfoPtr info = RADEONPTR(pScrn);
DRI2InfoRec dri2_info = { 0 };
#if DRI2INFOREC_VERSION >= 4
const char *driverNames[1];
#endif
if (!info->useEXA) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DRI2 requires EXA\n");
......@@ -359,6 +797,21 @@ radeon_dri2_screen_init(ScreenPtr pScreen)
dri2_info.DestroyBuffer = radeon_dri2_destroy_buffer;
#endif
dri2_info.CopyRegion = radeon_dri2_copy_region;
#if DRI2INFOREC_VERSION >= 4
if (info->dri->pKernelDRMVersion->version_minor >= 4) {
dri2_info.version = 4;
dri2_info.ScheduleSwap = radeon_dri2_schedule_swap;
dri2_info.GetMSC = radeon_dri2_get_msc;
dri2_info.ScheduleWaitMSC = radeon_dri2_schedule_wait_msc;
dri2_info.numDrivers = 1;
dri2_info.driverNames = driverNames;
driverNames[0] = dri2_info.driverName;
} else {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for sync extension\n");
}
#endif
info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
return info->dri2.enabled;
}
......
......@@ -39,4 +39,10 @@ Bool radeon_dri2_screen_init(ScreenPtr pScreen);
void radeon_dri2_close_screen(ScreenPtr pScreen);
#endif
int drmmode_get_crtc_id(xf86CrtcPtr crtc);
xf86CrtcPtr radeon_covering_crtc(ScrnInfoPtr pScrn, BoxPtr box,
xf86CrtcPtr desired, BoxPtr crtc_box_ret);
void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
unsigned int tv_usec, void *event_data);
#endif
......@@ -493,10 +493,6 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n");
goto fail;
}
if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n");
goto fail;
}
info->dri2.enabled = FALSE;
info->dri->pKernelDRMVersion = drmGetVersion(info->dri->drmFD);
......@@ -506,6 +502,12 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
goto fail;
}
if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n");
goto fail;
}
{
struct drm_radeon_gem_info mminfo;
......
......@@ -162,8 +162,7 @@ radeon_pick_best_crtc(ScrnInfoPtr pScrn,
return best_crtc;
}
#ifndef HAVE_XF86CRTCCLIPVIDEOHELPER
static xf86CrtcPtr
xf86CrtcPtr
radeon_covering_crtc(ScrnInfoPtr pScrn,
BoxPtr box,
xf86CrtcPtr desired,
......@@ -198,6 +197,7 @@ radeon_covering_crtc(ScrnInfoPtr pScrn,