Files
xf86-video-ati/src/radeon_dri2.c
Alan Coopersmith 008e3482de Strip trailing whitespace from source files
Performed with: `git ls-files | xargs perl -i -p -e 's{[ \t]+$}{}'`

`git diff -w` & `git diff -b` show no diffs from this change

Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
Part-of: <https://gitlab.freedesktop.org/xorg/driver/xf86-video-ati/-/merge_requests/32>
2025-08-12 17:22:33 -07:00

1488 lines
44 KiB
C

/*
* Copyright 2008 Kristian Høgsberg
* Copyright 2008 Jérôme Glisse
*
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation on the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
* THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radeon.h"
#include "radeon_dri2.h"
#include "radeon_video.h"
#ifdef DRI2
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "radeon_bo_helper.h"
#include "radeon_version.h"
#include "radeon_bo_gem.h"
#include <list.h>
#include <xf86Priv.h>
#include <X11/extensions/dpmsconst.h>
#define FALLBACK_SWAP_DELAY 16
#include "radeon_glamor.h"
typedef DRI2BufferPtr BufferPtr;
struct dri2_buffer_priv {
PixmapPtr pixmap;
unsigned int attachment;
unsigned int refcnt;
};
struct dri2_window_priv {
xf86CrtcPtr crtc;
int vblank_delta;
};
static DevPrivateKeyRec dri2_window_private_key_rec;
#define dri2_window_private_key (&dri2_window_private_key_rec)
#define get_dri2_window_priv(window) \
((struct dri2_window_priv*) \
dixLookupPrivate(&(window)->devPrivates, dri2_window_private_key))
/* Get GEM flink name for a pixmap */
static Bool
radeon_get_flink_name(RADEONEntPtr pRADEONEnt, PixmapPtr pixmap, uint32_t *name)
{
struct radeon_buffer *bo = radeon_get_pixmap_bo(pixmap);
struct drm_gem_flink flink;
if (bo && !(bo->flags & RADEON_BO_FLAGS_GBM) &&
radeon_gem_get_kernel_name(bo->bo.radeon, name) == 0)
return TRUE;
if (radeon_get_pixmap_handle(pixmap, &flink.handle)) {
if (drmIoctl(pRADEONEnt->fd, DRM_IOCTL_GEM_FLINK, &flink) != 0)
return FALSE;
*name = flink.name;
return TRUE;
}
return FALSE;
}
static BufferPtr
radeon_dri2_create_buffer2(ScreenPtr pScreen,
DrawablePtr drawable,
unsigned int attachment,
unsigned int format)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
RADEONInfoPtr info = RADEONPTR(pScrn);
BufferPtr buffers;
struct dri2_buffer_priv *privates;
PixmapPtr pixmap;
int flags;
unsigned front_width;
unsigned aligned_width = drawable->width;
unsigned height = drawable->height;
Bool is_glamor_pixmap = FALSE;
int depth;
int cpp;
if (format) {
depth = format;
switch (depth) {
case 15:
cpp = 2;
break;
case 24:
case 30:
cpp = 4;
break;
default:
cpp = depth / 8;
}
} else {
depth = drawable->depth;
cpp = drawable->bitsPerPixel / 8;
}
front_width = pScreen->GetScreenPixmap(pScreen)->drawable.width;
pixmap = NULL;
if (attachment == DRI2BufferFrontLeft) {
uint32_t handle;
pixmap = get_drawable_pixmap(drawable);
if (pScreen != pixmap->drawable.pScreen)
pixmap = NULL;
else if (info->use_glamor && !radeon_get_pixmap_handle(pixmap, &handle)) {
is_glamor_pixmap = TRUE;
aligned_width = pixmap->drawable.width;
height = pixmap->drawable.height;
pixmap = NULL;
} else
pixmap->refcnt++;
}
if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) {
/* tile the back buffer */
switch(attachment) {
case DRI2BufferDepth:
/* macro is the preferred setting, but the 2D detiling for software
* fallbacks in mesa still has issues on some configurations
*/
if (info->ChipFamily >= CHIP_FAMILY_R600) {
if (info->allowColorTiling2D) {
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
} else {
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
}
if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
} else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
else
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
flags |= RADEON_CREATE_PIXMAP_DEPTH;
break;
case DRI2BufferDepthStencil:
/* macro is the preferred setting, but the 2D detiling for software
* fallbacks in mesa still has issues on some configurations
*/
if (info->ChipFamily >= CHIP_FAMILY_R600) {
if (info->allowColorTiling2D) {
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
} else {
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
}
if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
} else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
else
flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
flags |= RADEON_CREATE_PIXMAP_DEPTH;
break;
case DRI2BufferBackLeft:
case DRI2BufferBackRight:
case DRI2BufferFrontLeft:
case DRI2BufferFrontRight:
case DRI2BufferFakeFrontLeft:
case DRI2BufferFakeFrontRight:
if (info->ChipFamily >= CHIP_FAMILY_R600) {
if (info->allowColorTiling2D) {
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
} else {
flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
}
} else
flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
break;
default:
flags = 0;
}
if (aligned_width == front_width)
aligned_width = pScrn->virtualX;
pixmap = (*pScreen->CreatePixmap)(pScreen,
aligned_width,
height,
depth,
flags | RADEON_CREATE_PIXMAP_DRI2);
}
if (!pixmap)
return NULL;
buffers = calloc(1, sizeof *buffers);
if (!buffers)
goto error;
if (!info->use_glamor) {
info->exa_force_create = TRUE;
exaMoveInPixmap(pixmap);
info->exa_force_create = FALSE;
if (!exaGetPixmapDriverPrivate(pixmap)) {
/* this happen if pixmap is non accelerable */
goto error;
}
} else if (is_glamor_pixmap) {
pixmap = radeon_glamor_set_pixmap_bo(drawable, pixmap);
pixmap->refcnt++;
/* The copy operation from radeon_glamor_set_pixmap_bo needs to
* be flushed to the kernel driver before the client starts
* using the pixmap storage for direct rendering.
*/
radeon_cs_flush_indirect(pScrn);
}
if (!radeon_get_flink_name(pRADEONEnt, pixmap, &buffers->name))
goto error;
privates = calloc(1, sizeof(struct dri2_buffer_priv));
if (!privates)
goto error;
buffers->attachment = attachment;
buffers->pitch = pixmap->devKind;
buffers->cpp = cpp;
buffers->driverPrivate = privates;
buffers->format = format;
buffers->flags = 0; /* not tiled */
privates->pixmap = pixmap;
privates->attachment = attachment;
privates->refcnt = 1;
return buffers;
error:
free(buffers);
(*pScreen->DestroyPixmap)(pixmap);
return NULL;
}
static void
radeon_dri2_destroy_buffer2(ScreenPtr pScreen,
DrawablePtr drawable, BufferPtr buffers)
{
if(buffers)
{
struct dri2_buffer_priv *private = buffers->driverPrivate;
/* Trying to free an already freed buffer is unlikely to end well */
if (private->refcnt == 0) {
ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"Attempted to destroy previously destroyed buffer.\
This is a programming error\n");
return;
}
private->refcnt--;
if (private->refcnt == 0)
{
if (private->pixmap)
(*pScreen->DestroyPixmap)(private->pixmap);
free(buffers->driverPrivate);
free(buffers);
}
}
}
static inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
{
if (drawable->type == DRAWABLE_PIXMAP)
return (PixmapPtr)drawable;
else {
struct _Window *pWin = (struct _Window *)drawable;
return drawable->pScreen->GetWindowPixmap(pWin);
}
}
static void
radeon_dri2_copy_region2(ScreenPtr pScreen,
DrawablePtr drawable,
RegionPtr region,
BufferPtr dest_buffer,
BufferPtr src_buffer)
{
struct dri2_buffer_priv *src_private = src_buffer->driverPrivate;
struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate;
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
DrawablePtr src_drawable;
DrawablePtr dst_drawable;
RegionPtr copy_clip;
GCPtr gc;
RADEONInfoPtr info = RADEONPTR(pScrn);
Bool vsync;
Bool translate = FALSE;
int off_x = 0, off_y = 0;
src_drawable = &src_private->pixmap->drawable;
dst_drawable = &dst_private->pixmap->drawable;
if (src_private->attachment == DRI2BufferFrontLeft) {
if (drawable->pScreen != pScreen) {
src_drawable = DRI2UpdatePrime(drawable, src_buffer);
if (!src_drawable)
return;
} else
src_drawable = drawable;
}
if (dst_private->attachment == DRI2BufferFrontLeft) {
if (drawable->pScreen != pScreen) {
dst_drawable = DRI2UpdatePrime(drawable, dest_buffer);
if (!dst_drawable)
return;
if (dst_drawable != drawable)
translate = TRUE;
} else
dst_drawable = drawable;
}
if (translate && drawable->type == DRAWABLE_WINDOW) {
PixmapPtr pPix = GetDrawablePixmap(drawable);
off_x = drawable->x - pPix->screen_x;
off_y = drawable->y - pPix->screen_y;
}
gc = GetScratchGC(dst_drawable->depth, pScreen);
copy_clip = REGION_CREATE(pScreen, NULL, 0);
REGION_COPY(pScreen, copy_clip, region);
if (translate) {
REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y);
}
(*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0);
ValidateGC(dst_drawable, gc);
vsync = info->accel_state->vsync;
/* Driver option "SwapbuffersWait" defines if we vsync DRI2 copy-swaps. */
info->accel_state->vsync = info->swapBuffersWait;
info->accel_state->force = TRUE;
(*gc->ops->CopyArea)(src_drawable, dst_drawable, gc,
0, 0, drawable->width, drawable->height, off_x, off_y);
info->accel_state->force = FALSE;
info->accel_state->vsync = vsync;
FreeScratchGC(gc);
}
enum DRI2FrameEventType {
DRI2_SWAP,
DRI2_FLIP,
DRI2_WAITMSC,
};
typedef struct _DRI2FrameEvent {
XID drawable_id;
ClientPtr client;
enum DRI2FrameEventType type;
unsigned frame;
xf86CrtcPtr crtc;
OsTimerPtr timer;
uintptr_t drm_queue_seq;
/* for swaps & flips only */
DRI2SwapEventPtr event_complete;
void *event_data;
DRI2BufferPtr front;
DRI2BufferPtr back;
} DRI2FrameEventRec, *DRI2FrameEventPtr;
static int DRI2InfoCnt;
static void
radeon_dri2_ref_buffer(BufferPtr buffer)
{
struct dri2_buffer_priv *private = buffer->driverPrivate;
private->refcnt++;
}
static void
radeon_dri2_unref_buffer(BufferPtr buffer)
{
if (buffer) {
struct dri2_buffer_priv *private = buffer->driverPrivate;
DrawablePtr draw = &private->pixmap->drawable;
radeon_dri2_destroy_buffer2(draw->pScreen, draw, buffer);
}
}
static void
radeon_dri2_client_state_changed(CallbackListPtr *ClientStateCallback, pointer data, pointer calldata)
{
NewClientInfoRec *clientinfo = calldata;
ClientPtr pClient = clientinfo->client;
switch (pClient->clientState) {
case ClientStateRetained:
case ClientStateGone:
radeon_drm_abort_client(pClient);
break;
default:
break;
}
}
/*
* Get current frame count delta for the specified drawable and CRTC
*/
static uint32_t radeon_get_msc_delta(DrawablePtr pDraw, xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
if (pDraw && pDraw->type == DRAWABLE_WINDOW)
return drmmode_crtc->interpolated_vblanks +
get_dri2_window_priv((WindowPtr)pDraw)->vblank_delta;
return drmmode_crtc->interpolated_vblanks;
}
/*
* Get current frame count and timestamp of the specified CRTC
*/
static Bool radeon_dri2_get_crtc_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
if (!radeon_crtc_is_enabled(crtc) ||
drmmode_crtc_get_ust_msc(crtc, ust, msc) != Success) {
/* CRTC is not running, extrapolate MSC and timestamp */
ScrnInfoPtr scrn = crtc->scrn;
RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
CARD64 now, delta_t, delta_seq;
if (!drmmode_crtc->dpms_last_ust)
return FALSE;
if (drmmode_get_current_ust(pRADEONEnt->fd, &now) != 0) {
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
"%s cannot get current time\n", __func__);
return FALSE;
}
delta_t = now - drmmode_crtc->dpms_last_ust;
delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
delta_seq /= 1000000;
*ust = drmmode_crtc->dpms_last_ust;
delta_t = delta_seq * 1000000;
delta_t /= drmmode_crtc->dpms_last_fps;
*ust += delta_t;
*msc = drmmode_crtc->dpms_last_seq;
*msc += delta_seq;
}
*msc += drmmode_crtc->interpolated_vblanks;
return TRUE;
}
static
xf86CrtcPtr radeon_dri2_drawable_crtc(DrawablePtr pDraw)
{
ScreenPtr pScreen = pDraw->pScreen;
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
xf86CrtcPtr crtc = radeon_pick_best_crtc(pScrn, TRUE,
pDraw->x, pDraw->x + pDraw->width,
pDraw->y, pDraw->y + pDraw->height);
if (pDraw->type == DRAWABLE_WINDOW) {
struct dri2_window_priv *priv = get_dri2_window_priv((WindowPtr)pDraw);
if (!crtc) {
crtc = priv->crtc;
} else if (priv->crtc && priv->crtc != crtc) {
CARD64 ust, mscold, mscnew;
if (radeon_dri2_get_crtc_msc(priv->crtc, &ust, &mscold) &&
radeon_dri2_get_crtc_msc(crtc, &ust, &mscnew))
priv->vblank_delta += mscold - mscnew;
}
priv->crtc = crtc;
}
return crtc;
}
static void
radeon_dri2_flip_event_abort(xf86CrtcPtr crtc, void *event_data)
{
if (crtc)
RADEONPTR(crtc->scrn)->drmmode.dri2_flipping = FALSE;
free(event_data);
}
static void
radeon_dri2_flip_event_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
void *event_data)
{
DRI2FrameEventPtr flip = event_data;
ScrnInfoPtr scrn = crtc->scrn;
unsigned tv_sec, tv_usec;
DrawablePtr drawable;
ScreenPtr screen;
int status;
PixmapPtr pixmap;
status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient,
M_ANY, DixWriteAccess);
if (status != Success)
goto abort;
frame += radeon_get_msc_delta(drawable, crtc);
screen = scrn->pScreen;
pixmap = screen->GetScreenPixmap(screen);
xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
"%s:%d fevent[%p] width %d pitch %d (/4 %d)\n",
__func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4);
tv_sec = usec / 1000000;
tv_usec = usec % 1000000;
/* We assume our flips arrive in order, so we don't check the frame */
switch (flip->type) {
case DRI2_SWAP:
/* Check for too small vblank count of pageflip completion, taking wraparound
* into account. This usually means some defective kms pageflip completion,
* causing wrong (msc, ust) return values and possible visual corruption.
*/
if ((frame < flip->frame) && (flip->frame - frame < 5)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"%s: Pageflip completion event has impossible msc %u < target_msc %u\n",
__func__, frame, flip->frame);
/* All-Zero values signal failure of (msc, ust) timestamping to client. */
frame = tv_sec = tv_usec = 0;
}
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;
}
abort:
radeon_dri2_flip_event_abort(crtc, event_data);
}
static Bool
radeon_dri2_schedule_flip(xf86CrtcPtr crtc, ClientPtr client,
DrawablePtr draw, DRI2BufferPtr front,
DRI2BufferPtr back, DRI2SwapEventPtr func,
void *data, unsigned int target_msc)
{
ScrnInfoPtr scrn = crtc->scrn;
RADEONInfoPtr info = RADEONPTR(scrn);
struct dri2_buffer_priv *back_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;
flip_info->frame = target_msc;
flip_info->crtc = crtc;
xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
"%s:%d fevent[%p]\n", __func__, __LINE__, flip_info);
/* Page flip the full screen buffer */
back_priv = back->driverPrivate;
if (radeon_do_pageflip(scrn, client, back_priv->pixmap,
RADEON_DRM_QUEUE_ID_DEFAULT, flip_info, crtc,
radeon_dri2_flip_event_handler,
radeon_dri2_flip_event_abort, FLIP_VSYNC,
target_msc - radeon_get_msc_delta(draw, crtc))) {
info->drmmode.dri2_flipping = TRUE;
return TRUE;
}
return FALSE;
}
static Bool
update_front(DrawablePtr draw, DRI2BufferPtr front)
{
PixmapPtr pixmap;
ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen);
RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
RADEONInfoPtr info = RADEONPTR(scrn);
struct dri2_buffer_priv *priv = front->driverPrivate;
pixmap = get_drawable_pixmap(draw);
pixmap->refcnt++;
if (!info->use_glamor)
exaMoveInPixmap(pixmap);
if (!radeon_get_flink_name(pRADEONEnt, pixmap, &front->name)) {
(*draw->pScreen->DestroyPixmap)(pixmap);
return FALSE;
}
(*draw->pScreen->DestroyPixmap)(priv->pixmap);
front->pitch = pixmap->devKind;
front->cpp = pixmap->drawable.bitsPerPixel / 8;
priv->pixmap = pixmap;
return TRUE;
}
static Bool
can_exchange(ScrnInfoPtr pScrn, DrawablePtr draw,
DRI2BufferPtr front, DRI2BufferPtr back)
{
struct dri2_buffer_priv *front_priv = front->driverPrivate;
struct dri2_buffer_priv *back_priv = back->driverPrivate;
PixmapPtr front_pixmap;
PixmapPtr back_pixmap = back_priv->pixmap;
if (!update_front(draw, front))
return FALSE;
front_pixmap = front_priv->pixmap;
if (front_pixmap->drawable.width != back_pixmap->drawable.width)
return FALSE;
if (front_pixmap->drawable.height != back_pixmap->drawable.height)
return FALSE;
if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel)
return FALSE;
if (front_pixmap->devKind != back_pixmap->devKind)
return FALSE;
return TRUE;
}
static Bool
can_flip(xf86CrtcPtr crtc, DrawablePtr draw,
DRI2BufferPtr front, DRI2BufferPtr back)
{
ScrnInfoPtr pScrn = crtc->scrn;
RADEONInfoPtr info = RADEONPTR(pScrn);
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
int num_crtcs_on;
int i;
if (draw->type != DRAWABLE_WINDOW ||
!info->allowPageFlip ||
info->sprites_visible > 0 ||
info->drmmode.present_flipping ||
!pScrn->vtSema ||
!DRI2CanFlip(draw))
return FALSE;
for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) {
if (drmmode_crtc_can_flip(config->crtc[i]))
num_crtcs_on++;
}
return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back);
}
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;
#ifdef USE_GLAMOR
RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(draw->pScreen));
#endif
RegionRec region;
int tmp;
region.extents.x1 = region.extents.y1 = 0;
region.extents.x2 = front_priv->pixmap->drawable.width;
region.extents.y2 = front_priv->pixmap->drawable.height;
region.data = NULL;
DamageRegionAppend(&front_priv->pixmap->drawable, &region);
/* Swap BO names so DRI works */
tmp = front->name;
front->name = back->name;
back->name = tmp;
/* Swap pixmap privates */
#ifdef USE_GLAMOR
if (info->use_glamor) {
struct radeon_pixmap *front_pix, *back_pix;
front_pix = radeon_get_pixmap_private(front_priv->pixmap);
back_pix = radeon_get_pixmap_private(back_priv->pixmap);
radeon_set_pixmap_private(front_priv->pixmap, back_pix);
radeon_set_pixmap_private(back_priv->pixmap, front_pix);
radeon_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
} else
#endif
{
struct radeon_exa_pixmap_priv driver_priv = *(struct radeon_exa_pixmap_priv*)
exaGetPixmapDriverPrivate(front_priv->pixmap);
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(front_priv->pixmap) =
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(back_priv->pixmap);
*(struct radeon_exa_pixmap_priv*)exaGetPixmapDriverPrivate(back_priv->pixmap) =
driver_priv;
}
DamageRegionProcessPending(&front_priv->pixmap->drawable);
}
static void radeon_dri2_frame_event_abort(xf86CrtcPtr crtc, void *event_data)
{
DRI2FrameEventPtr event = event_data;
TimerCancel(event->timer);
TimerFree(event->timer);
radeon_dri2_unref_buffer(event->front);
radeon_dri2_unref_buffer(event->back);
free(event);
}
static void radeon_dri2_frame_event_handler(xf86CrtcPtr crtc, uint32_t seq,
uint64_t usec, void *event_data)
{
DRI2FrameEventPtr event = event_data;
ScrnInfoPtr scrn = crtc->scrn;
DrawablePtr drawable;
int status;
int swap_type;
BoxRec box;
RegionRec region;
status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
M_ANY, DixWriteAccess);
if (status != Success)
goto cleanup;
seq += radeon_get_msc_delta(drawable, crtc);
switch (event->type) {
case DRI2_FLIP:
if (can_flip(crtc, drawable, event->front, event->back) &&
radeon_dri2_schedule_flip(crtc,
event->client,
drawable,
event->front,
event->back,
event->event_complete,
event->event_data,
event->frame)) {
radeon_dri2_exchange_buffers(drawable, event->front, event->back);
break;
}
/* else fall through to exchange/blit */
case DRI2_SWAP:
if (DRI2CanExchange(drawable) &&
can_exchange(scrn, drawable, 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_region2(drawable->pScreen, drawable, &region,
event->front, event->back);
swap_type = DRI2_BLIT_COMPLETE;
}
DRI2SwapComplete(event->client, drawable, seq, usec / 1000000,
usec % 1000000, swap_type, event->event_complete,
event->event_data);
break;
case DRI2_WAITMSC:
DRI2WaitMSCComplete(event->client, drawable, seq, usec / 1000000,
usec % 1000000);
break;
default:
/* Unknown type */
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"%s: unknown vblank event received\n", __func__);
break;
}
cleanup:
radeon_dri2_frame_event_abort(crtc, event_data);
}
/*
* This function should be called on a disabled CRTC only (i.e., CRTC
* in DPMS-off state). It will calculate the delay necessary to reach
* target_msc from present time if the CRTC were running.
*/
static
CARD32 radeon_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 *target_msc,
CARD64 divisor, CARD64 remainder)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
ScrnInfoPtr pScrn = crtc->scrn;
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
int nominal_frame_rate = drmmode_crtc->dpms_last_fps;
CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust;
uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq;
CARD64 now, target_time, delta_t;
int64_t d, delta_seq;
int ret;
CARD32 d_ms;
if (!last_vblank_ust) {
*target_msc = 0;
return FALLBACK_SWAP_DELAY;
}
ret = drmmode_get_current_ust(pRADEONEnt->fd, &now);
if (ret) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"%s cannot get current time\n", __func__);
*target_msc = 0;
return FALLBACK_SWAP_DELAY;
}
delta_seq = *target_msc - last_vblank_seq;
delta_seq *= 1000000;
target_time = last_vblank_ust;
target_time += delta_seq / nominal_frame_rate;
d = target_time - now;
if (d < 0) {
/* we missed the event, adjust target_msc, do the divisor magic */
CARD64 current_msc = last_vblank_seq;
delta_t = now - last_vblank_ust;
delta_seq = delta_t * nominal_frame_rate;
current_msc += delta_seq / 1000000;
current_msc &= 0xffffffff;
if (divisor == 0) {
*target_msc = current_msc;
d = 0;
} else {
*target_msc = current_msc - (current_msc % divisor) + remainder;
if ((current_msc % divisor) >= remainder)
*target_msc += divisor;
*target_msc &= 0xffffffff;
delta_seq = *target_msc - last_vblank_seq;
delta_seq *= 1000000;
target_time = last_vblank_ust;
target_time += delta_seq / nominal_frame_rate;
d = target_time - now;
}
}
/*
* convert delay to milliseconds and add margin to prevent the client
* from coming back early (due to timer granularity and rounding
* errors) and getting the same MSC it just got
*/
d_ms = (CARD32)d / 1000;
if ((CARD32)d - d_ms * 1000 > 0)
d_ms += 2;
else
d_ms++;
return d_ms;
}
/*
* Get current interpolated frame count and frame count timestamp, based on
* drawable's crtc.
*/
static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
{
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
/* Drawable not displayed, make up a value */
if (!crtc) {
*ust = 0;
*msc = 0;
return TRUE;
}
if (!radeon_dri2_get_crtc_msc(crtc, ust, msc))
return FALSE;
if (draw && draw->type == DRAWABLE_WINDOW)
*msc += get_dri2_window_priv((WindowPtr)draw)->vblank_delta;
*msc &= 0xffffffff;
return TRUE;
}
static
CARD32 radeon_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data)
{
DRI2FrameEventPtr event_info = (DRI2FrameEventPtr)data;
xf86CrtcPtr crtc = event_info->crtc;
ScrnInfoPtr scrn;
RADEONEntPtr pRADEONEnt;
CARD64 drm_now;
int ret;
CARD64 delta_t, delta_seq, frame;
drmmode_crtc_private_ptr drmmode_crtc;
/*
* This is emulated event, so its time is current time, which we
* have to get in DRM-compatible form (which is a bit messy given
* the information that we have at this point). Can't use now argument
* because DRM event time may come from monotonic clock, while
* DIX timer facility uses real-time clock.
*/
if (!event_info->crtc) {
ErrorF("%s no crtc\n", __func__);
if (event_info->drm_queue_seq)
radeon_drm_abort_entry(event_info->drm_queue_seq);
else
radeon_dri2_frame_event_abort(NULL, data);
return 0;
}
scrn = crtc->scrn;
pRADEONEnt = RADEONEntPriv(scrn);
drmmode_crtc = event_info->crtc->driver_private;
ret = drmmode_get_current_ust(pRADEONEnt->fd, &drm_now);
if (ret) {
xf86DrvMsg(scrn->scrnIndex, X_ERROR,
"%s cannot get current time\n", __func__);
if (event_info->drm_queue_seq) {
drmmode_crtc->drmmode->event_context.
vblank_handler(pRADEONEnt->fd, 0, 0, 0,
(void*)event_info->drm_queue_seq);
drmmode_crtc->wait_flip_nesting_level++;
radeon_drm_queue_handle_deferred(crtc);
} else {
radeon_dri2_frame_event_handler(crtc, 0, 0, data);
}
return 0;
}
/*
* calculate the frame number from current time
* that would come from CRTC if it were running
*/
delta_t = drm_now - (CARD64)drmmode_crtc->dpms_last_ust;
delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
delta_seq /= 1000000;
frame = (CARD64)drmmode_crtc->dpms_last_seq + delta_seq;
if (event_info->drm_queue_seq) {
drmmode_crtc->drmmode->event_context.
vblank_handler(pRADEONEnt->fd, frame, drm_now / 1000000,
drm_now % 1000000,
(void*)event_info->drm_queue_seq);
drmmode_crtc->wait_flip_nesting_level++;
radeon_drm_queue_handle_deferred(crtc);
} else {
radeon_dri2_frame_event_handler(crtc, frame, drm_now, data);
}
return 0;
}
static
void radeon_dri2_schedule_event(CARD32 delay, DRI2FrameEventPtr event_info)
{
event_info->timer = TimerSet(NULL, 0, delay, radeon_dri2_deferred_event,
event_info);
if (delay == 0) {
CARD32 now = GetTimeInMillis();
radeon_dri2_deferred_event(event_info->timer, now, event_info);
}
}
/*
* 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 = xf86ScreenToScrn(screen);
DRI2FrameEventPtr wait_info = NULL;
uintptr_t drm_queue_seq = 0;
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
uint32_t msc_delta;
uint32_t seq;
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)
goto out_complete;
msc_delta = radeon_get_msc_delta(draw, crtc);
wait_info = calloc(1, sizeof(DRI2FrameEventRec));
if (!wait_info)
goto out_complete;
wait_info->drawable_id = draw->id;
wait_info->client = client;
wait_info->type = DRI2_WAITMSC;
wait_info->crtc = crtc;
/*
* CRTC is in DPMS off state, calculate wait time from current time,
* target_msc and last vblank time/sequence when CRTC was turned off
*/
if (!radeon_crtc_is_enabled(crtc)) {
CARD32 delay;
target_msc -= msc_delta;
delay = radeon_dri2_extrapolate_msc_delay(crtc, &target_msc,
divisor, remainder);
radeon_dri2_schedule_event(delay, wait_info);
DRI2BlockClient(client, draw);
return TRUE;
}
/* Get current count */
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
current_msc = seq + msc_delta;
current_msc &= 0xffffffff;
drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
wait_info, radeon_dri2_frame_event_handler,
radeon_dri2_frame_event_abort, FALSE);
if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"Allocating DRM queue event entry failed.\n");
goto out_complete;
}
wait_info->drm_queue_seq = drm_queue_seq;
/*
* 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;
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT,
target_msc - msc_delta, drm_queue_seq, NULL,
NULL)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
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.
*/
target_msc = current_msc - (current_msc % divisor) + remainder - msc_delta;
/*
* 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)
target_msc += divisor;
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT,
target_msc, drm_queue_seq, NULL, NULL)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"get vblank counter failed: %s\n", strerror(errno));
goto out_complete;
}
DRI2BlockClient(client, draw);
return TRUE;
out_complete:
if (wait_info)
radeon_dri2_deferred_event(NULL, 0, wait_info);
else
DRI2WaitMSCComplete(client, draw, 0, 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 = xf86ScreenToScrn(screen);
xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw);
uint32_t msc_delta;
drmVBlankSeqType type;
uint32_t seq;
int flip = 0;
DRI2FrameEventPtr swap_info = NULL;
uintptr_t drm_queue_seq;
CARD64 current_msc, event_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;
/* radeon_dri2_frame_event_handler will get called some unknown time in the
* future with these buffers. Take a reference to ensure that they won't
* get destroyed before then.
*/
radeon_dri2_ref_buffer(front);
radeon_dri2_ref_buffer(back);
/* either off-screen or CRTC not usable... just complete the swap */
if (!crtc)
goto blit_fallback;
msc_delta = radeon_get_msc_delta(draw, crtc);
swap_info = calloc(1, sizeof(DRI2FrameEventRec));
if (!swap_info)
goto blit_fallback;
swap_info->type = DRI2_SWAP;
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;
swap_info->crtc = crtc;
drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
swap_info, radeon_dri2_frame_event_handler,
radeon_dri2_frame_event_abort, FALSE);
if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"Allocating DRM queue entry failed.\n");
goto blit_fallback;
}
swap_info->drm_queue_seq = drm_queue_seq;
/*
* CRTC is in DPMS off state, fallback to blit, but calculate
* wait time from current time, target_msc and last vblank
* time/sequence when CRTC was turned off
*/
if (!radeon_crtc_is_enabled(crtc)) {
CARD32 delay;
*target_msc -= msc_delta;
delay = radeon_dri2_extrapolate_msc_delay(crtc, target_msc,
divisor, remainder);
*target_msc += msc_delta;
*target_msc &= 0xffffffff;
radeon_dri2_schedule_event(delay, swap_info);
return TRUE;
}
/* Get current count */
if (!drmmode_wait_vblank(crtc, DRM_VBLANK_RELATIVE, 0, 0, NULL, &seq)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"first get vblank counter failed: %s\n",
strerror(errno));
goto blit_fallback;
}
current_msc = seq + msc_delta;
current_msc &= 0xffffffff;
/* Flips need to be submitted one frame before */
if (can_flip(crtc, draw, front, back)) {
swap_info->type = DRI2_FLIP;
flip = 1;
}
/* Correct target_msc by 'flip' if swap_info->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) {
type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
/* If non-pageflipping, but blitting/exchanging, we need to use
* DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
* on.
*/
if (flip == 0)
type |= DRM_VBLANK_NEXTONMISS;
/* 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;
if (!drmmode_wait_vblank(crtc, type, *target_msc - msc_delta,
drm_queue_seq, NULL, &seq)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"divisor 0 get vblank counter failed: %s\n",
strerror(errno));
goto blit_fallback;
}
*target_msc = seq + flip + msc_delta;
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.
*/
type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
if (flip == 0)
type |= DRM_VBLANK_NEXTONMISS;
event_msc = current_msc - (current_msc % divisor) + remainder - msc_delta;
/*
* 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 (event_msc <= current_msc)
event_msc += divisor;
/* Account for 1 frame extra pageflip delay if flip > 0 */
event_msc -= flip;
if (!drmmode_wait_vblank(crtc, type, event_msc, drm_queue_seq, NULL, &seq)) {
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 = seq + flip + msc_delta;
*target_msc &= 0xffffffff;
swap_info->frame = *target_msc;
return TRUE;
blit_fallback:
if (swap_info) {
swap_info->type = DRI2_SWAP;
radeon_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info);
} else {
box.x1 = 0;
box.y1 = 0;
box.x2 = draw->width;
box.y2 = draw->height;
REGION_INIT(pScreen, &region, &box, 0);
radeon_dri2_copy_region2(draw->pScreen, draw, &region, front, back);
DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
radeon_dri2_unref_buffer(front);
radeon_dri2_unref_buffer(back);
}
*target_msc = 0; /* offscreen, so zero out target vblank count */
return TRUE;
}
Bool
radeon_dri2_screen_init(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn);
RADEONInfoPtr info = RADEONPTR(pScrn);
DRI2InfoRec dri2_info = { 0 };
const char *driverNames[2];
Bool scheduling_works = TRUE;
if (!info->dri2.available)
return FALSE;
info->dri2.device_name = drmGetDeviceNameFromFd(pRADEONEnt->fd);
if ( (info->ChipFamily >= CHIP_FAMILY_TAHITI) ) {
dri2_info.driverName = SI_DRIVER_NAME;
} else if ( (info->ChipFamily >= CHIP_FAMILY_R600) ) {
dri2_info.driverName = R600_DRIVER_NAME;
} else if ( (info->ChipFamily >= CHIP_FAMILY_R300) ) {
dri2_info.driverName = R300_DRIVER_NAME;
} else if ( info->ChipFamily >= CHIP_FAMILY_R200 ) {
dri2_info.driverName = R200_DRIVER_NAME;
} else {
dri2_info.driverName = RADEON_DRIVER_NAME;
}
dri2_info.fd = pRADEONEnt->fd;
dri2_info.deviceName = info->dri2.device_name;
if (info->dri2.pKernelDRMVersion->version_minor < 4) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for "
"sync extension\n");
scheduling_works = FALSE;
}
if (scheduling_works && info->drmmode.count_crtcs > 2) {
#ifdef DRM_CAP_VBLANK_HIGH_CRTC
uint64_t cap_value;
if (drmGetCap(pRADEONEnt->fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel "
"for VBLANKs on CRTC > 1\n");
scheduling_works = FALSE;
} else if (!cap_value) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Your kernel does not "
"handle VBLANKs on CRTC > 1\n");
scheduling_works = FALSE;
}
#else
xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need to rebuild against a "
"newer libdrm to handle VBLANKs on CRTC > 1\n");
scheduling_works = FALSE;
#endif
}
if (scheduling_works) {
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 = ARRAY_SIZE(driverNames);
dri2_info.driverNames = driverNames;
driverNames[0] = dri2_info.driverName;
if (info->ChipFamily >= CHIP_FAMILY_R300)
driverNames[1] = driverNames[0];
else
driverNames[1] = NULL; /* no VDPAU support */
if (DRI2InfoCnt == 0) {
if (!dixRegisterPrivateKey(dri2_window_private_key,
PRIVATE_WINDOW,
sizeof(struct dri2_window_priv))) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"Failed to get DRI2 window private\n");
return FALSE;
}
AddCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
}
DRI2InfoCnt++;
}
dri2_info.version = 9;
dri2_info.CreateBuffer2 = radeon_dri2_create_buffer2;
dri2_info.DestroyBuffer2 = radeon_dri2_destroy_buffer2;
dri2_info.CopyRegion2 = radeon_dri2_copy_region2;
info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
return info->dri2.enabled;
}
void radeon_dri2_close_screen(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
RADEONInfoPtr info = RADEONPTR(pScrn);
if (--DRI2InfoCnt == 0)
DeleteCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
DRI2CloseScreen(pScreen);
drmFree(info->dri2.device_name);
}
#endif /* DRI2 */