requires drm 2.7.0 or higher

Signed-off-by: Alex Deucher <alexdeucher at gmail.com>
Signed-off-by: Jerome Glisse <jglisse at redhat.com>
---
 src/drmmode_display.c |   78 ++++++++++++++++++++-
 src/drmmode_display.h |    4 +
 src/radeon.h          |    2 +
 src/radeon_dri2.c     |  188 ++++++++++++++++++++++++++++++++++++++++++++-----
 src/radeon_dri2.h     |    2 +
 src/radeon_kms.c      |    8 ++
 6 files changed, 264 insertions(+), 18 deletions(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 37010cd..f6a4217 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -29,6 +29,7 @@
 #include "config.h"
 #endif

+#include <errno.h>
 #ifdef XF86DRM_MODE
 #include <sys/ioctl.h>
 #include "micmap.h"
@@ -1195,6 +1196,25 @@ drmmode_vblank_handler(int fd, unsigned int frame, 
unsigned int tv_sec,
 }

 static void
+drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec,
+                    unsigned int tv_usec, void *event_data)
+{
+       drmmode_ptr drmmode = event_data;
+
+       drmmode->flip_count--;
+       if (drmmode->flip_count > 0)
+               return;
+
+       drmModeRmFB(drmmode->fd, drmmode->old_fb_id);
+
+       if (drmmode->event_data == NULL)
+               return;
+
+       radeon_dri2_flip_event_handler(frame, tv_sec, tv_usec, 
drmmode->event_data);
+}
+
+
+static void
 drm_wakeup_handler(pointer data, int err, pointer p)
 {
        drmmode_ptr drmmode = data;
@@ -1236,8 +1256,9 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr 
drmmode, int cpp)
        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;
+       drmmode->event_context.page_flip_handler = drmmode_flip_handler;
        if (info->dri->pKernelDRMVersion->version_minor >= 4) {
+               drmmode->flip_count = 0;
                AddGeneralSocket(drmmode->fd);
                RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
                                drm_wakeup_handler, drmmode);
@@ -1476,4 +1497,59 @@ void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr 
drmmode)
 #endif
 }

+Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void 
*data)
+{
+       RADEONInfoPtr info = RADEONPTR(scrn);
+       xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+       drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private;
+       drmmode_ptr drmmode = drmmode_crtc->drmmode;
+       unsigned int pitch = scrn->displayWidth * 
info->CurrentLayout.pixel_bytes;
+       int i, old_fb_id;
+
+       /*
+        * Create a new handle for the back buffer
+        */
+       old_fb_id = drmmode->fb_id;
+       if (drmModeAddFB(drmmode->fd, scrn->virtualX, scrn->virtualY,
+                        scrn->depth, scrn->bitsPerPixel, pitch,
+                        new_front->handle, &drmmode->fb_id))
+               goto error_out;
+
+       /*
+        * Queue flips on all enabled CRTCs
+        * Note that if/when we get per-CRTC buffers, we'll have to update this.
+        * Right now it assumes a single shared fb across all CRTCs, with the
+        * kernel fixing up the offset of each CRTC as necessary.
+        *
+        * Also, flips queued on disabled or incorrectly configured displays
+        * may never complete; this is a configuration error.
+        */
+       for (i = 0; i < config->num_crtc; i++) {
+               if (!config->crtc[i]->enabled)
+                       continue;
+
+               drmmode->event_data = data;
+               drmmode->flip_count++;
+               drmmode_crtc = config->crtc[i]->driver_private;
+               if (drmModePageFlip(drmmode->fd, 
drmmode_crtc->mode_crtc->crtc_id,
+                                   drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, 
drmmode)) {
+                       xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                                  "flip queue failed: %s\n", strerror(errno));
+                       goto error_undo;
+               }
+       }
+
+       drmmode->old_fb_id = old_fb_id;
+       return TRUE;
+
+error_undo:
+       drmModeRmFB(drmmode->fd, drmmode->fb_id);
+       drmmode->fb_id = old_fb_id;
+
+error_out:
+       xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
+                  strerror(errno));
+       return FALSE;
+}
+
 #endif
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index a9891b2..2226075 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -39,6 +39,7 @@
 typedef struct {
   int fd;
   unsigned fb_id;
+  unsigned old_fb_id;
   drmModeResPtr mode_res;
   drmModeFBPtr mode_fb;
   int cpp;
@@ -50,6 +51,7 @@ typedef struct {
 #endif
   drmEventContext event_context;
   int flip_count;
+  void *event_data;
 } drmmode_rec, *drmmode_ptr;

 typedef struct {
@@ -95,6 +97,8 @@ extern Bool drmmode_setup_colormap(ScreenPtr pScreen, 
ScrnInfoPtr pScrn);
 extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode);
 extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode);

+Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void 
*data);
+
 #endif

 #endif
diff --git a/src/radeon.h b/src/radeon.h
index 21c6d1c..8ace884 100644
--- a/src/radeon.h
+++ b/src/radeon.h
@@ -1071,6 +1071,8 @@ typedef struct {
     struct radeon_bo *bicubic_bo;
     void             *bicubic_memory;
     int               bicubic_offset;
+    /* kms pageflipping */
+    Bool allowPageFlip;
 } RADEONInfoRec, *RADEONInfoPtr;

 #define RADEONWaitForFifo(pScrn, entries)                              \
diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
index 2668812..fb0d83d 100644
--- a/src/radeon_dri2.c
+++ b/src/radeon_dri2.c
@@ -549,12 +549,100 @@ radeon_dri2_unref_buffer(BufferPtr buffer)
     }
 }

+static Bool
+radeon_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client,
+                         DrawablePtr draw, DRI2BufferPtr front,
+                         DRI2BufferPtr back, DRI2SwapEventPtr func,
+                         void *data)
+{
+    struct dri2_buffer_priv *back_priv;
+    struct radeon_exa_pixmap_priv *exa_priv;
+    DRI2FrameEventPtr flip_info;
+
+    flip_info = calloc(1, sizeof(DRI2FrameEventRec));
+    if (!flip_info)
+       return FALSE;
+
+    flip_info->drawable_id = draw->id;
+    flip_info->client = client;
+    flip_info->type = DRI2_SWAP;
+    flip_info->event_complete = func;
+    flip_info->event_data = data;
+    xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p]\n", __func__, 
__LINE__, flip_info);
+
+    /* Page flip the full screen buffer */
+    back_priv = back->driverPrivate;
+    exa_priv = exaGetPixmapDriverPrivate(back_priv->pixmap);
+    return radeon_do_pageflip(scrn, exa_priv->bo, flip_info);
+}
+
+static Bool
+can_exchange(DRI2BufferPtr front, DRI2BufferPtr back)
+{
+    struct dri2_buffer_priv *front_priv = front->driverPrivate;
+    struct dri2_buffer_priv *back_priv = back->driverPrivate;
+    PixmapPtr front_pixmap = front_priv->pixmap;
+    PixmapPtr back_pixmap = back_priv->pixmap;
+
+    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
+       return FALSE;
+
+    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
+       return FALSE;
+
+    /* XXX should we be checking depth instead of bpp? */
+#if 0
+    if (front_pixmap->drawable.depth != back_pixmap->drawable.depth)
+       return FALSE;
+#else
+    if (front_pixmap->drawable.bitsPerPixel != 
back_pixmap->drawable.bitsPerPixel)
+       return FALSE;
+#endif
+
+    return TRUE;
+}
+
+static void
+radeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, 
DRI2BufferPtr back)
+{
+    struct dri2_buffer_priv *front_priv = front->driverPrivate;
+    struct dri2_buffer_priv *back_priv = back->driverPrivate;
+    struct radeon_exa_pixmap_priv *front_radeon, *back_radeon;
+    ScreenPtr screen;
+    RADEONInfoPtr info;
+    struct radeon_bo *bo;
+    int tmp;
+
+    /* Swap BO names so DRI works */
+    tmp = front->name;
+    front->name = back->name;
+    back->name = tmp;
+
+    /* Swap pixmap bos */
+    front_radeon = exaGetPixmapDriverPrivate(front_priv->pixmap);
+    back_radeon = exaGetPixmapDriverPrivate(back_priv->pixmap);
+    bo = back_radeon->bo;
+    back_radeon->bo = front_radeon->bo;
+    front_radeon->bo = bo;
+
+    /* Do we need to update the Screen? */
+    screen = draw->pScreen;
+    info = RADEONPTR(xf86Screens[screen->myNum]);
+    if (front_radeon->bo == info->front_bo) {
+       radeon_bo_unref(info->front_bo);
+       info->front_bo = back_radeon->bo;
+       radeon_bo_ref(info->front_bo);
+       front_radeon = 
exaGetPixmapDriverPrivate(screen->GetScreenPixmap(screen));
+        front_radeon->bo = bo;
+    }
+}
+
 void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
                                      unsigned int tv_usec, void *event_data)
 {
     DRI2FrameEventPtr event = event_data;
+    RADEONInfoPtr info;
     DrawablePtr drawable;
-    ClientPtr client;
     ScreenPtr screen;
     ScrnInfoPtr scrn;
     int status;
@@ -563,7 +651,7 @@ void radeon_dri2_frame_event_handler(unsigned int frame, 
unsigned int tv_sec,
     RegionRec region;

     if (!event->valid)
-        goto cleanup;
+       goto cleanup;

     status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
                                M_ANY, DixWriteAccess);
@@ -572,25 +660,45 @@ void radeon_dri2_frame_event_handler(unsigned int frame, 
unsigned int tv_sec,

     screen = drawable->pScreen;
     scrn = xf86Screens[screen->myNum];
-    client = event->client;
+    info = RADEONPTR(scrn);

     switch (event->type) {
     case DRI2_FLIP:
+       if (info->allowPageFlip &&
+           DRI2CanFlip(drawable) &&
+           can_exchange(event->front, event->back) &&
+           radeon_dri2_schedule_flip(scrn,
+                                     event->client,
+                                     drawable,
+                                     event->front,
+                                     event->back,
+                                     event->event_complete,
+                                     event->event_data)) {
+           radeon_dri2_exchange_buffers(drawable, event->front, event->back);
+           break;
+       }
+       /* else fall through to exchange/blit */
     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(client, drawable, frame, tv_sec, tv_usec,
+       if (DRI2CanExchange(drawable) &&
+           can_exchange(event->front, event->back)) {
+           radeon_dri2_exchange_buffers(drawable, event->front, event->back);
+           swap_type = DRI2_EXCHANGE_COMPLETE;
+       } else {
+           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(client, drawable, frame, tv_sec, tv_usec);
+        DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, tv_usec);
         break;
     default:
         /* Unknown type */
@@ -638,7 +746,7 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 
*ust, CARD64 *msc)
     RADEONInfoPtr info = RADEONPTR(scrn);
     drmVBlank vbl;
     int ret;
-    int crtc= radeon_dri2_drawable_crtc(draw);
+    int crtc = radeon_dri2_drawable_crtc(draw);

     /* Drawable not displayed, make up a value */
     if (crtc == -1) {
@@ -794,6 +902,46 @@ out_complete:
     return TRUE;
 }

+void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
+                                   unsigned int tv_usec, void *event_data)
+{
+    DRI2FrameEventPtr flip = event_data;
+    DrawablePtr drawable;
+    ScreenPtr screen;
+    ScrnInfoPtr scrn;
+    int status;
+    PixmapPtr pixmap;
+
+    status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient,
+                              M_ANY, DixWriteAccess);
+    if (status != Success) {
+       free(flip);
+       return;
+    }
+
+    screen = drawable->pScreen;
+    scrn = xf86Screens[screen->myNum];
+
+    pixmap = screen->GetScreenPixmap(screen);
+    xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p] width %d pitch %d 
(/4 %d)\n",
+              __func__, __LINE__, flip, pixmap->drawable.width, 
pixmap->devKind, pixmap->devKind/4);
+
+    /* We assume our flips arrive in order, so we don't check the frame */
+    switch (flip->type) {
+    case DRI2_SWAP:
+       DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec,
+                        DRI2_FLIP_COMPLETE, flip->event_complete,
+                        flip->event_data);
+       break;
+    default:
+       xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event 
received\n", __func__);
+       /* Unknown type */
+       break;
+    }
+
+    free(flip);
+}
+
 /*
  * ScheduleSwap is responsible for requesting a DRM vblank event for the
  * appropriate frame.
@@ -877,6 +1025,15 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,
     }

     current_msc = vbl.reply.sequence;
+
+    /* Flips need to be submitted one frame before */
+    if (info->allowPageFlip &&
+       DRI2CanFlip(draw) &&
+       can_exchange(front, back)) {
+       swap_type = DRI2_FLIP;
+       flip = 1;
+    }
+
     swap_info->type = swap_type;

     /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP.
@@ -893,9 +1050,6 @@ static int radeon_dri2_schedule_swap(ClientPtr client, 
DrawablePtr draw,
      */
     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.
diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h
index 688530f..7995286 100644
--- a/src/radeon_dri2.h
+++ b/src/radeon_dri2.h
@@ -44,5 +44,7 @@ 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);
+void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec,
+                                   unsigned int tv_usec, void *event_data);

 #endif
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index b94544e..7773d02 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -70,6 +70,7 @@ const OptionInfoRec RADEONOptions_KMS[] = {
     { OPTION_EXA_VSYNC,      "EXAVSync",         OPTV_BOOLEAN, {0}, FALSE },
     { OPTION_EXA_PIXMAPS,    "EXAPixmaps",      OPTV_BOOLEAN,   {0}, FALSE },
     { OPTION_ZAPHOD_HEADS,   "ZaphodHeads",      OPTV_STRING,  {0}, FALSE },
+    { OPTION_PAGE_FLIP,      "EnablePageFlip",   OPTV_BOOLEAN, {0}, FALSE },
     { -1,                    NULL,               OPTV_NONE,    {0}, FALSE }
 };

@@ -616,6 +617,13 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
         "KMS Color Tiling: %sabled\n", info->allowColorTiling ? "en" : "dis");

+    if (info->dri->pKernelDRMVersion->version_minor >= 7) {
+       info->allowPageFlip = xf86ReturnOptValBool(info->Options,
+                                                  OPTION_PAGE_FLIP, TRUE);
+       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+                  "KMS Pageflipping: %sabled\n", info->allowPageFlip ? "en" : 
"dis");
+    }
+
     if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == 
FALSE) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup 
failed\n");
        goto fail;
-- 
1.7.1.1

Reply via email to