[PATCH] kms: add support for the MSC swap & sync API

Jerome Glisse jglisse at redhat.com
Fri May 7 08:35:09 PDT 2010


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.
---
 src/drmmode_display.c |   52 ++++++
 src/drmmode_display.h |    3 +
 src/radeon_dri2.c     |  453 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/radeon_dri2.h     |    6 +
 src/radeon_video.c    |    4 +-
 5 files changed, 516 insertions(+), 2 deletions(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index ea361d6..2a56d76 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -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,6 +1177,23 @@ 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)
 {
@@ -1178,6 +1222,14 @@ 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;
+	AddGeneralSocket(drmmode->fd);
+	RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA,
+			drm_wakeup_handler, drmmode);
+
 	return TRUE;
 }
 
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 86caabb..a9891b2 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -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;
diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
index 103972f..0e6292e 100644
--- a/src/radeon_dri2.c
+++ b/src/radeon_dri2.c
@@ -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;
 }
diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h
index c391e73..688530f 100644
--- a/src/radeon_dri2.h
+++ b/src/radeon_dri2.h
@@ -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
diff --git a/src/radeon_video.c b/src/radeon_video.c
index 5e2a723..7b5f6de 100644
--- a/src/radeon_video.c
+++ b/src/radeon_video.c
@@ -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,
     return best_crtc;
 }
 
+#ifndef HAVE_XF86CRTCCLIPVIDEOHELPER
 static Bool
 radeon_crtc_clip_video_helper(ScrnInfoPtr pScrn,
 			      xf86CrtcPtr *crtc_ret,
-- 
1.7.0.1


--DocE+STaALJfprDB--


More information about the xorg-driver-ati mailing list