radeon_dri2.c revision 3ed65abb
1de2362d3Smrg/*
2de2362d3Smrg * Copyright 2008 Kristian Høgsberg
3de2362d3Smrg * Copyright 2008 Jérôme Glisse
4de2362d3Smrg *
5de2362d3Smrg * All Rights Reserved.
6de2362d3Smrg *
7de2362d3Smrg * Permission is hereby granted, free of charge, to any person obtaining
8de2362d3Smrg * a copy of this software and associated documentation files (the
9de2362d3Smrg * "Software"), to deal in the Software without restriction, including
10de2362d3Smrg * without limitation on the rights to use, copy, modify, merge,
11de2362d3Smrg * publish, distribute, sublicense, and/or sell copies of the Software,
12de2362d3Smrg * and to permit persons to whom the Software is furnished to do so,
13de2362d3Smrg * subject to the following conditions:
14de2362d3Smrg *
15de2362d3Smrg * The above copyright notice and this permission notice (including the
16de2362d3Smrg * next paragraph) shall be included in all copies or substantial
17de2362d3Smrg * portions of the Software.
18de2362d3Smrg *
19de2362d3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20de2362d3Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21de2362d3Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22de2362d3Smrg * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
23de2362d3Smrg * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24de2362d3Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25de2362d3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26de2362d3Smrg * DEALINGS IN THE SOFTWARE.
27de2362d3Smrg */
28de2362d3Smrg#ifdef HAVE_CONFIG_H
29de2362d3Smrg#include "config.h"
30de2362d3Smrg#endif
31de2362d3Smrg
3218781e08Smrg#include "radeon.h"
3318781e08Smrg#include "radeon_dri2.h"
3418781e08Smrg#include "radeon_video.h"
3518781e08Smrg
3618781e08Smrg#ifdef DRI2
3718781e08Smrg
38de2362d3Smrg#include <sys/types.h>
39de2362d3Smrg#include <sys/stat.h>
40de2362d3Smrg#include <fcntl.h>
41de2362d3Smrg#include <errno.h>
42de2362d3Smrg
4318781e08Smrg#include "radeon_bo_helper.h"
44de2362d3Smrg#include "radeon_version.h"
4518781e08Smrg#include "radeon_list.h"
46de2362d3Smrg
477821949aSmrg#include "radeon_bo_gem.h"
48de2362d3Smrg
4918781e08Smrg#include <xf86Priv.h>
503ed65abbSmrg#include <X11/extensions/dpmsconst.h>
51de2362d3Smrg
5218781e08Smrg#if DRI2INFOREC_VERSION >= 9
5318781e08Smrg#define USE_DRI2_PRIME
547821949aSmrg#endif
55de2362d3Smrg
5618781e08Smrg#define FALLBACK_SWAP_DELAY 16
5718781e08Smrg
5818781e08Smrg#include "radeon_glamor.h"
5918781e08Smrg
60de2362d3Smrgtypedef DRI2BufferPtr BufferPtr;
61de2362d3Smrg
62de2362d3Smrgstruct dri2_buffer_priv {
63de2362d3Smrg    PixmapPtr   pixmap;
64de2362d3Smrg    unsigned int attachment;
65de2362d3Smrg    unsigned int refcnt;
66de2362d3Smrg};
67de2362d3Smrg
68de2362d3Smrg
6918781e08Smrgstruct dri2_window_priv {
7018781e08Smrg    xf86CrtcPtr crtc;
7118781e08Smrg    int vblank_delta;
7218781e08Smrg};
730d16fef4Smrg
7418781e08Smrgstatic DevPrivateKeyRec dri2_window_private_key_rec;
7518781e08Smrg#define dri2_window_private_key (&dri2_window_private_key_rec)
760d16fef4Smrg
7718781e08Smrg#define get_dri2_window_priv(window) \
7818781e08Smrg    ((struct dri2_window_priv*) \
7918781e08Smrg     dixLookupPrivate(&(window)->devPrivates, dri2_window_private_key))
800d16fef4Smrg
810d16fef4Smrg
8218781e08Smrg/* Get GEM flink name for a pixmap */
8318781e08Smrgstatic Bool
8418781e08Smrgradeon_get_flink_name(RADEONInfoPtr info, PixmapPtr pixmap, uint32_t *name)
8518781e08Smrg{
8618781e08Smrg    struct radeon_bo *bo = radeon_get_pixmap_bo(pixmap);
8718781e08Smrg    struct drm_gem_flink flink;
880d16fef4Smrg
8918781e08Smrg    if (bo)
9018781e08Smrg	return radeon_gem_get_kernel_name(bo, name) == 0;
9118781e08Smrg
9218781e08Smrg    if (radeon_get_pixmap_handle(pixmap, &flink.handle)) {
9318781e08Smrg	if (drmIoctl(info->dri2.drm_fd, DRM_IOCTL_GEM_FLINK, &flink) != 0)
9418781e08Smrg	    return FALSE;
950d16fef4Smrg
9618781e08Smrg	*name = flink.name;
9718781e08Smrg	return TRUE;
980d16fef4Smrg    }
9918781e08Smrg
10018781e08Smrg    return FALSE;
1010d16fef4Smrg}
10218781e08Smrg
103de2362d3Smrgstatic BufferPtr
10418781e08Smrgradeon_dri2_create_buffer2(ScreenPtr pScreen,
10518781e08Smrg			   DrawablePtr drawable,
10618781e08Smrg			   unsigned int attachment,
10718781e08Smrg			   unsigned int format)
108de2362d3Smrg{
109de2362d3Smrg    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
110de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
111de2362d3Smrg    BufferPtr buffers;
112de2362d3Smrg    struct dri2_buffer_priv *privates;
11318781e08Smrg    PixmapPtr pixmap;
114de2362d3Smrg    int flags;
115de2362d3Smrg    unsigned front_width;
116de2362d3Smrg    uint32_t tiling = 0;
117de2362d3Smrg    unsigned aligned_width = drawable->width;
11818781e08Smrg    unsigned height = drawable->height;
11918781e08Smrg    Bool is_glamor_pixmap = FALSE;
12018781e08Smrg    int depth;
12118781e08Smrg    int cpp;
12218781e08Smrg
12318781e08Smrg    if (format) {
12418781e08Smrg	depth = format;
12518781e08Smrg
12618781e08Smrg	switch (depth) {
12718781e08Smrg	case 15:
12818781e08Smrg	    cpp = 2;
12918781e08Smrg	    break;
13018781e08Smrg	case 24:
13118781e08Smrg	    cpp = 4;
13218781e08Smrg	    break;
13318781e08Smrg	default:
13418781e08Smrg	    cpp = depth / 8;
13518781e08Smrg	}
13618781e08Smrg    } else {
13718781e08Smrg	depth = drawable->depth;
13818781e08Smrg	cpp = drawable->bitsPerPixel / 8;
13918781e08Smrg    }
140de2362d3Smrg
14118781e08Smrg    front_width = pScreen->GetScreenPixmap(pScreen)->drawable.width;
142de2362d3Smrg
14318781e08Smrg    pixmap = NULL;
144de2362d3Smrg
145de2362d3Smrg    if (attachment == DRI2BufferFrontLeft) {
14618781e08Smrg	uint32_t handle;
14718781e08Smrg
14818781e08Smrg        pixmap = get_drawable_pixmap(drawable);
14918781e08Smrg	if (pScreen != pixmap->drawable.pScreen)
15018781e08Smrg	    pixmap = NULL;
15118781e08Smrg	else if (info->use_glamor && !radeon_get_pixmap_handle(pixmap, &handle)) {
15218781e08Smrg	    is_glamor_pixmap = TRUE;
15318781e08Smrg	    aligned_width = pixmap->drawable.width;
15418781e08Smrg	    height = pixmap->drawable.height;
15518781e08Smrg	    pixmap = NULL;
15618781e08Smrg	} else
15718781e08Smrg	    pixmap->refcnt++;
15818781e08Smrg    }
15918781e08Smrg
16018781e08Smrg    if (!pixmap && (is_glamor_pixmap || attachment != DRI2BufferFrontLeft)) {
161de2362d3Smrg	/* tile the back buffer */
162de2362d3Smrg	switch(attachment) {
163de2362d3Smrg	case DRI2BufferDepth:
164de2362d3Smrg	    /* macro is the preferred setting, but the 2D detiling for software
165de2362d3Smrg	     * fallbacks in mesa still has issues on some configurations
166de2362d3Smrg	     */
167de2362d3Smrg	    if (info->ChipFamily >= CHIP_FAMILY_R600) {
168de2362d3Smrg		if (info->allowColorTiling2D) {
169de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
170de2362d3Smrg		} else {
171de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
172de2362d3Smrg		}
173de2362d3Smrg		if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
174de2362d3Smrg		    flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
17518781e08Smrg	    } else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
17618781e08Smrg		flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
17718781e08Smrg	    else
178de2362d3Smrg		flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
179de2362d3Smrg	    if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
180de2362d3Smrg		flags |= RADEON_CREATE_PIXMAP_DEPTH;
181de2362d3Smrg	    break;
182de2362d3Smrg	case DRI2BufferDepthStencil:
183de2362d3Smrg	    /* macro is the preferred setting, but the 2D detiling for software
184de2362d3Smrg	     * fallbacks in mesa still has issues on some configurations
185de2362d3Smrg	     */
186de2362d3Smrg	    if (info->ChipFamily >= CHIP_FAMILY_R600) {
187de2362d3Smrg		if (info->allowColorTiling2D) {
188de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
189de2362d3Smrg		} else {
190de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
191de2362d3Smrg		}
192de2362d3Smrg		if (info->ChipFamily >= CHIP_FAMILY_CEDAR)
193de2362d3Smrg		    flags |= RADEON_CREATE_PIXMAP_SZBUFFER;
19418781e08Smrg	    } else if (cpp == 2 && info->ChipFamily >= CHIP_FAMILY_R300)
19518781e08Smrg		flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE;
19618781e08Smrg	    else
197de2362d3Smrg		flags = RADEON_CREATE_PIXMAP_TILING_MACRO | RADEON_CREATE_PIXMAP_TILING_MICRO;
198de2362d3Smrg	    if (IS_R200_3D || info->ChipFamily == CHIP_FAMILY_RV200 || info->ChipFamily == CHIP_FAMILY_RADEON)
199de2362d3Smrg		flags |= RADEON_CREATE_PIXMAP_DEPTH;
200de2362d3Smrg
201de2362d3Smrg	    break;
202de2362d3Smrg	case DRI2BufferBackLeft:
203de2362d3Smrg	case DRI2BufferBackRight:
20418781e08Smrg	case DRI2BufferFrontLeft:
20518781e08Smrg	case DRI2BufferFrontRight:
206de2362d3Smrg	case DRI2BufferFakeFrontLeft:
207de2362d3Smrg	case DRI2BufferFakeFrontRight:
208de2362d3Smrg	    if (info->ChipFamily >= CHIP_FAMILY_R600) {
209de2362d3Smrg		if (info->allowColorTiling2D) {
210de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
211de2362d3Smrg		} else {
212de2362d3Smrg			flags = RADEON_CREATE_PIXMAP_TILING_MICRO;
213de2362d3Smrg		}
214de2362d3Smrg	    } else
215de2362d3Smrg		flags = RADEON_CREATE_PIXMAP_TILING_MACRO;
216de2362d3Smrg	    break;
217de2362d3Smrg	default:
218de2362d3Smrg	    flags = 0;
219de2362d3Smrg	}
220de2362d3Smrg
221de2362d3Smrg	if (flags & RADEON_CREATE_PIXMAP_TILING_MICRO)
222de2362d3Smrg	    tiling |= RADEON_TILING_MICRO;
22318781e08Smrg	if (flags & RADEON_CREATE_PIXMAP_TILING_MICRO_SQUARE)
22418781e08Smrg	    tiling |= RADEON_TILING_MICRO_SQUARE;
225de2362d3Smrg	if (flags & RADEON_CREATE_PIXMAP_TILING_MACRO)
226de2362d3Smrg	    tiling |= RADEON_TILING_MACRO;
227de2362d3Smrg
22818781e08Smrg	if (aligned_width == front_width)
22918781e08Smrg	    aligned_width = pScrn->virtualX;
230de2362d3Smrg
23118781e08Smrg	pixmap = (*pScreen->CreatePixmap)(pScreen,
23218781e08Smrg					  aligned_width,
23318781e08Smrg					  height,
23418781e08Smrg					  depth,
23518781e08Smrg					  flags | RADEON_CREATE_PIXMAP_DRI2);
236de2362d3Smrg    }
237de2362d3Smrg
238de2362d3Smrg    buffers = calloc(1, sizeof *buffers);
239de2362d3Smrg    if (buffers == NULL)
240de2362d3Smrg        goto error;
241de2362d3Smrg
24218781e08Smrg    if (pixmap) {
24318781e08Smrg	if (!info->use_glamor) {
24418781e08Smrg	    info->exa_force_create = TRUE;
24518781e08Smrg	    exaMoveInPixmap(pixmap);
24618781e08Smrg	    info->exa_force_create = FALSE;
24718781e08Smrg	    if (exaGetPixmapDriverPrivate(pixmap) == NULL) {
24818781e08Smrg		/* this happen if pixmap is non accelerable */
24918781e08Smrg		goto error;
25018781e08Smrg	    }
25118781e08Smrg	} else if (is_glamor_pixmap) {
25218781e08Smrg	    pixmap = radeon_glamor_set_pixmap_bo(drawable, pixmap);
25318781e08Smrg	    pixmap->refcnt++;
25418781e08Smrg	}
25518781e08Smrg
25618781e08Smrg	if (!radeon_get_flink_name(info, pixmap, &buffers->name))
25718781e08Smrg	    goto error;
258de2362d3Smrg    }
259de2362d3Smrg
260de2362d3Smrg    privates = calloc(1, sizeof(struct dri2_buffer_priv));
261de2362d3Smrg    if (privates == NULL)
262de2362d3Smrg        goto error;
263de2362d3Smrg
264de2362d3Smrg    buffers->attachment = attachment;
26518781e08Smrg    if (pixmap) {
26618781e08Smrg	buffers->pitch = pixmap->devKind;
26718781e08Smrg	buffers->cpp = cpp;
26818781e08Smrg    }
269de2362d3Smrg    buffers->driverPrivate = privates;
270de2362d3Smrg    buffers->format = format;
271de2362d3Smrg    buffers->flags = 0; /* not tiled */
272de2362d3Smrg    privates->pixmap = pixmap;
273de2362d3Smrg    privates->attachment = attachment;
274de2362d3Smrg    privates->refcnt = 1;
275de2362d3Smrg
276de2362d3Smrg    return buffers;
277de2362d3Smrg
278de2362d3Smrgerror:
279de2362d3Smrg    free(buffers);
280de2362d3Smrg    if (pixmap)
281de2362d3Smrg        (*pScreen->DestroyPixmap)(pixmap);
282de2362d3Smrg    return NULL;
283de2362d3Smrg}
284de2362d3Smrg
28518781e08SmrgDRI2BufferPtr
28618781e08Smrgradeon_dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment,
28718781e08Smrg			   unsigned int format)
288de2362d3Smrg{
28918781e08Smrg	return radeon_dri2_create_buffer2(pDraw->pScreen, pDraw,
29018781e08Smrg					  attachment, format);
2917821949aSmrg}
29218781e08Smrg
293de2362d3Smrgstatic void
29418781e08Smrgradeon_dri2_destroy_buffer2(ScreenPtr pScreen,
29518781e08Smrg			    DrawablePtr drawable, BufferPtr buffers)
296de2362d3Smrg{
297de2362d3Smrg    if(buffers)
298de2362d3Smrg    {
299de2362d3Smrg        struct dri2_buffer_priv *private = buffers->driverPrivate;
300de2362d3Smrg
301de2362d3Smrg        /* Trying to free an already freed buffer is unlikely to end well */
302de2362d3Smrg        if (private->refcnt == 0) {
303de2362d3Smrg            ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
304de2362d3Smrg
305de2362d3Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
306de2362d3Smrg                       "Attempted to destroy previously destroyed buffer.\
307de2362d3Smrg This is a programming error\n");
308de2362d3Smrg            return;
309de2362d3Smrg        }
310de2362d3Smrg
311de2362d3Smrg        private->refcnt--;
312de2362d3Smrg        if (private->refcnt == 0)
313de2362d3Smrg        {
31418781e08Smrg	    if (private->pixmap)
31518781e08Smrg                (*pScreen->DestroyPixmap)(private->pixmap);
316de2362d3Smrg
317de2362d3Smrg            free(buffers->driverPrivate);
318de2362d3Smrg            free(buffers);
319de2362d3Smrg        }
320de2362d3Smrg    }
321de2362d3Smrg}
322de2362d3Smrg
32318781e08Smrgvoid
32418781e08Smrgradeon_dri2_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buf)
32518781e08Smrg{
32618781e08Smrg    radeon_dri2_destroy_buffer2(pDraw->pScreen, pDraw, buf);
32718781e08Smrg}
32818781e08Smrg
32918781e08Smrg
33018781e08Smrgstatic inline PixmapPtr GetDrawablePixmap(DrawablePtr drawable)
33118781e08Smrg{
33218781e08Smrg    if (drawable->type == DRAWABLE_PIXMAP)
33318781e08Smrg        return (PixmapPtr)drawable;
33418781e08Smrg    else {
33518781e08Smrg        struct _Window *pWin = (struct _Window *)drawable;
33618781e08Smrg        return drawable->pScreen->GetWindowPixmap(pWin);
33718781e08Smrg    }
33818781e08Smrg}
339de2362d3Smrgstatic void
34018781e08Smrgradeon_dri2_copy_region2(ScreenPtr pScreen,
34118781e08Smrg			 DrawablePtr drawable,
34218781e08Smrg			 RegionPtr region,
34318781e08Smrg			 BufferPtr dest_buffer,
34418781e08Smrg			 BufferPtr src_buffer)
345de2362d3Smrg{
346de2362d3Smrg    struct dri2_buffer_priv *src_private = src_buffer->driverPrivate;
347de2362d3Smrg    struct dri2_buffer_priv *dst_private = dest_buffer->driverPrivate;
348de2362d3Smrg    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
349de2362d3Smrg    DrawablePtr src_drawable;
350de2362d3Smrg    DrawablePtr dst_drawable;
351de2362d3Smrg    RegionPtr copy_clip;
352de2362d3Smrg    GCPtr gc;
353de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
354de2362d3Smrg    Bool vsync;
35518781e08Smrg    Bool translate = FALSE;
35618781e08Smrg    int off_x = 0, off_y = 0;
35718781e08Smrg    PixmapPtr dst_ppix;
35818781e08Smrg
35918781e08Smrg    dst_ppix = dst_private->pixmap;
36018781e08Smrg    src_drawable = &src_private->pixmap->drawable;
36118781e08Smrg    dst_drawable = &dst_private->pixmap->drawable;
362de2362d3Smrg
363de2362d3Smrg    if (src_private->attachment == DRI2BufferFrontLeft) {
36418781e08Smrg#ifdef USE_DRI2_PRIME
36518781e08Smrg	if (drawable->pScreen != pScreen) {
36618781e08Smrg	    src_drawable = DRI2UpdatePrime(drawable, src_buffer);
36718781e08Smrg	    if (!src_drawable)
36818781e08Smrg		return;
36918781e08Smrg	} else
37018781e08Smrg#endif
37118781e08Smrg	    src_drawable = drawable;
372de2362d3Smrg    }
373de2362d3Smrg    if (dst_private->attachment == DRI2BufferFrontLeft) {
37418781e08Smrg#ifdef USE_DRI2_PRIME
37518781e08Smrg	if (drawable->pScreen != pScreen) {
37618781e08Smrg	    dst_drawable = DRI2UpdatePrime(drawable, dest_buffer);
37718781e08Smrg	    if (!dst_drawable)
37818781e08Smrg		return;
37918781e08Smrg	    dst_ppix = (PixmapPtr)dst_drawable;
38018781e08Smrg	    if (dst_drawable != drawable)
38118781e08Smrg		translate = TRUE;
38218781e08Smrg	} else
38318781e08Smrg#endif
38418781e08Smrg	    dst_drawable = drawable;
38518781e08Smrg    }
38618781e08Smrg
38718781e08Smrg    if (translate && drawable->type == DRAWABLE_WINDOW) {
38818781e08Smrg	PixmapPtr pPix = GetDrawablePixmap(drawable);
38918781e08Smrg
39018781e08Smrg	off_x = drawable->x - pPix->screen_x;
39118781e08Smrg	off_y = drawable->y - pPix->screen_y;
392de2362d3Smrg    }
393de2362d3Smrg    gc = GetScratchGC(dst_drawable->depth, pScreen);
394de2362d3Smrg    copy_clip = REGION_CREATE(pScreen, NULL, 0);
395de2362d3Smrg    REGION_COPY(pScreen, copy_clip, region);
39618781e08Smrg
39718781e08Smrg    if (translate) {
39818781e08Smrg	REGION_TRANSLATE(pScreen, copy_clip, off_x, off_y);
39918781e08Smrg    }
40018781e08Smrg
401de2362d3Smrg    (*gc->funcs->ChangeClip) (gc, CT_REGION, copy_clip, 0);
402de2362d3Smrg    ValidateGC(dst_drawable, gc);
403de2362d3Smrg
404de2362d3Smrg    /* If this is a full buffer swap or frontbuffer flush, throttle on the
405de2362d3Smrg     * previous one
406de2362d3Smrg     */
407de2362d3Smrg    if (dst_private->attachment == DRI2BufferFrontLeft) {
408de2362d3Smrg	if (REGION_NUM_RECTS(region) == 1) {
409de2362d3Smrg	    BoxPtr extents = REGION_EXTENTS(pScreen, region);
410de2362d3Smrg
411de2362d3Smrg	    if (extents->x1 == 0 && extents->y1 == 0 &&
412de2362d3Smrg		extents->x2 == drawable->width &&
413de2362d3Smrg		extents->y2 == drawable->height) {
41418781e08Smrg		struct radeon_bo *bo = radeon_get_pixmap_bo(dst_ppix);
415de2362d3Smrg
41618781e08Smrg		if (bo)
41718781e08Smrg		    radeon_bo_wait(bo);
418de2362d3Smrg	    }
419de2362d3Smrg	}
420de2362d3Smrg    }
421de2362d3Smrg
422de2362d3Smrg    vsync = info->accel_state->vsync;
423de2362d3Smrg
424de2362d3Smrg    /* Driver option "SwapbuffersWait" defines if we vsync DRI2 copy-swaps. */
425de2362d3Smrg    info->accel_state->vsync = info->swapBuffersWait;
42618781e08Smrg    info->accel_state->force = TRUE;
427de2362d3Smrg
428de2362d3Smrg    (*gc->ops->CopyArea)(src_drawable, dst_drawable, gc,
42918781e08Smrg                         0, 0, drawable->width, drawable->height, off_x, off_y);
430de2362d3Smrg
43118781e08Smrg    info->accel_state->force = FALSE;
432de2362d3Smrg    info->accel_state->vsync = vsync;
433de2362d3Smrg
434de2362d3Smrg    FreeScratchGC(gc);
435de2362d3Smrg}
436de2362d3Smrg
43718781e08Smrgvoid
43818781e08Smrgradeon_dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
43918781e08Smrg			 DRI2BufferPtr pDstBuffer, DRI2BufferPtr pSrcBuffer)
44018781e08Smrg{
44118781e08Smrg    return radeon_dri2_copy_region2(pDraw->pScreen, pDraw, pRegion,
44218781e08Smrg				     pDstBuffer, pSrcBuffer);
44318781e08Smrg}
444de2362d3Smrg
445de2362d3Smrgenum DRI2FrameEventType {
446de2362d3Smrg    DRI2_SWAP,
447de2362d3Smrg    DRI2_FLIP,
448de2362d3Smrg    DRI2_WAITMSC,
449de2362d3Smrg};
450de2362d3Smrg
451de2362d3Smrgtypedef struct _DRI2FrameEvent {
452de2362d3Smrg    XID drawable_id;
453de2362d3Smrg    ClientPtr client;
454de2362d3Smrg    enum DRI2FrameEventType type;
45518781e08Smrg    unsigned frame;
45618781e08Smrg    xf86CrtcPtr crtc;
45718781e08Smrg    OsTimerPtr timer;
45818781e08Smrg    uintptr_t drm_queue_seq;
459de2362d3Smrg
460de2362d3Smrg    /* for swaps & flips only */
461de2362d3Smrg    DRI2SwapEventPtr event_complete;
462de2362d3Smrg    void *event_data;
463de2362d3Smrg    DRI2BufferPtr front;
464de2362d3Smrg    DRI2BufferPtr back;
465de2362d3Smrg} DRI2FrameEventRec, *DRI2FrameEventPtr;
466de2362d3Smrg
46718781e08Smrgstatic int DRI2InfoCnt;
468de2362d3Smrg
469de2362d3Smrgstatic void
470de2362d3Smrgradeon_dri2_ref_buffer(BufferPtr buffer)
471de2362d3Smrg{
472de2362d3Smrg    struct dri2_buffer_priv *private = buffer->driverPrivate;
473de2362d3Smrg    private->refcnt++;
474de2362d3Smrg}
475de2362d3Smrg
476de2362d3Smrgstatic void
477de2362d3Smrgradeon_dri2_unref_buffer(BufferPtr buffer)
478de2362d3Smrg{
479de2362d3Smrg    if (buffer) {
480de2362d3Smrg        struct dri2_buffer_priv *private = buffer->driverPrivate;
481de2362d3Smrg        radeon_dri2_destroy_buffer(&(private->pixmap->drawable), buffer);
482de2362d3Smrg    }
483de2362d3Smrg}
484de2362d3Smrg
485de2362d3Smrgstatic void
486de2362d3Smrgradeon_dri2_client_state_changed(CallbackListPtr *ClientStateCallback, pointer data, pointer calldata)
487de2362d3Smrg{
488de2362d3Smrg    NewClientInfoRec *clientinfo = calldata;
489de2362d3Smrg    ClientPtr pClient = clientinfo->client;
490de2362d3Smrg
491de2362d3Smrg    switch (pClient->clientState) {
492de2362d3Smrg    case ClientStateRetained:
493de2362d3Smrg    case ClientStateGone:
49418781e08Smrg        radeon_drm_abort_client(pClient);
495de2362d3Smrg        break;
496de2362d3Smrg    default:
497de2362d3Smrg        break;
498de2362d3Smrg    }
499de2362d3Smrg}
500de2362d3Smrg
50118781e08Smrg/*
50218781e08Smrg * Get current frame count delta for the specified drawable and CRTC
50318781e08Smrg */
50418781e08Smrgstatic uint32_t radeon_get_msc_delta(DrawablePtr pDraw, xf86CrtcPtr crtc)
50518781e08Smrg{
50618781e08Smrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
50718781e08Smrg
50818781e08Smrg    if (pDraw && pDraw->type == DRAWABLE_WINDOW)
50918781e08Smrg	return drmmode_crtc->interpolated_vblanks +
51018781e08Smrg	    get_dri2_window_priv((WindowPtr)pDraw)->vblank_delta;
51118781e08Smrg
51218781e08Smrg    return drmmode_crtc->interpolated_vblanks;
51318781e08Smrg}
51418781e08Smrg
51518781e08Smrg/*
51618781e08Smrg * Get current frame count and timestamp of the specified CRTC
51718781e08Smrg */
51818781e08Smrgstatic Bool radeon_dri2_get_crtc_msc(xf86CrtcPtr crtc, CARD64 *ust, CARD64 *msc)
51918781e08Smrg{
52018781e08Smrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
52118781e08Smrg
52218781e08Smrg    if (!radeon_crtc_is_enabled(crtc) ||
52318781e08Smrg	 drmmode_crtc_get_ust_msc(crtc, ust, msc) != Success) {
52418781e08Smrg	/* CRTC is not running, extrapolate MSC and timestamp */
52518781e08Smrg	ScrnInfoPtr scrn = crtc->scrn;
52618781e08Smrg	RADEONInfoPtr info = RADEONPTR(scrn);
52718781e08Smrg	CARD64 now, delta_t, delta_seq;
52818781e08Smrg
52918781e08Smrg	if (!drmmode_crtc->dpms_last_ust)
53018781e08Smrg	    return FALSE;
53118781e08Smrg
53218781e08Smrg	if (drmmode_get_current_ust(info->dri2.drm_fd, &now) != 0) {
53318781e08Smrg	    xf86DrvMsg(scrn->scrnIndex, X_ERROR,
53418781e08Smrg		       "%s cannot get current time\n", __func__);
53518781e08Smrg	    return FALSE;
53618781e08Smrg	}
53718781e08Smrg
53818781e08Smrg	delta_t = now - drmmode_crtc->dpms_last_ust;
53918781e08Smrg	delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
54018781e08Smrg	delta_seq /= 1000000;
54118781e08Smrg	*ust = drmmode_crtc->dpms_last_ust;
54218781e08Smrg	delta_t = delta_seq * 1000000;
54318781e08Smrg	delta_t /= drmmode_crtc->dpms_last_fps;
54418781e08Smrg	*ust += delta_t;
54518781e08Smrg	*msc = drmmode_crtc->dpms_last_seq;
54618781e08Smrg	*msc += delta_seq;
54718781e08Smrg    }
54818781e08Smrg
54918781e08Smrg    *msc += drmmode_crtc->interpolated_vblanks;
55018781e08Smrg
55118781e08Smrg    return TRUE;
55218781e08Smrg}
55318781e08Smrg
55418781e08Smrgstatic
55518781e08Smrgxf86CrtcPtr radeon_dri2_drawable_crtc(DrawablePtr pDraw, Bool consider_disabled)
556de2362d3Smrg{
557de2362d3Smrg    ScreenPtr pScreen = pDraw->pScreen;
558de2362d3Smrg    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
55918781e08Smrg    xf86CrtcPtr crtc = radeon_pick_best_crtc(pScrn, consider_disabled,
56018781e08Smrg					      pDraw->x, pDraw->x + pDraw->width,
56118781e08Smrg					      pDraw->y, pDraw->y + pDraw->height);
56218781e08Smrg
56318781e08Smrg    if (crtc && pDraw->type == DRAWABLE_WINDOW) {
56418781e08Smrg	struct dri2_window_priv *priv = get_dri2_window_priv((WindowPtr)pDraw);
56518781e08Smrg
56618781e08Smrg	if (priv->crtc && priv->crtc != crtc) {
56718781e08Smrg	    CARD64 ust, mscold, mscnew;
56818781e08Smrg
56918781e08Smrg	    if (radeon_dri2_get_crtc_msc(priv->crtc, &ust, &mscold) &&
57018781e08Smrg		radeon_dri2_get_crtc_msc(crtc, &ust, &mscnew))
57118781e08Smrg		priv->vblank_delta += mscold - mscnew;
57218781e08Smrg	}
57318781e08Smrg
57418781e08Smrg	priv->crtc = crtc;
57518781e08Smrg    }
57618781e08Smrg
57718781e08Smrg    return crtc;
57818781e08Smrg}
57918781e08Smrg
58018781e08Smrgstatic void
58118781e08Smrgradeon_dri2_flip_event_abort(xf86CrtcPtr crtc, void *event_data)
58218781e08Smrg{
5833ed65abbSmrg    if (crtc)
5843ed65abbSmrg	RADEONPTR(crtc->scrn)->drmmode.dri2_flipping = FALSE;
58518781e08Smrg
58618781e08Smrg    free(event_data);
58718781e08Smrg}
58818781e08Smrg
58918781e08Smrgstatic void
59018781e08Smrgradeon_dri2_flip_event_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
59118781e08Smrg			       void *event_data)
59218781e08Smrg{
59318781e08Smrg    DRI2FrameEventPtr flip = event_data;
59418781e08Smrg    ScrnInfoPtr scrn = crtc->scrn;
59518781e08Smrg    unsigned tv_sec, tv_usec;
59618781e08Smrg    DrawablePtr drawable;
59718781e08Smrg    ScreenPtr screen;
59818781e08Smrg    int status;
59918781e08Smrg    PixmapPtr pixmap;
60018781e08Smrg
60118781e08Smrg    status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient,
60218781e08Smrg			       M_ANY, DixWriteAccess);
60318781e08Smrg    if (status != Success)
60418781e08Smrg	goto abort;
60518781e08Smrg
60618781e08Smrg    frame += radeon_get_msc_delta(drawable, crtc);
6070d16fef4Smrg
60818781e08Smrg    screen = scrn->pScreen;
60918781e08Smrg    pixmap = screen->GetScreenPixmap(screen);
61018781e08Smrg    xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
61118781e08Smrg		   "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n",
61218781e08Smrg		   __func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4);
61318781e08Smrg
61418781e08Smrg    tv_sec = usec / 1000000;
61518781e08Smrg    tv_usec = usec % 1000000;
61618781e08Smrg
61718781e08Smrg    /* We assume our flips arrive in order, so we don't check the frame */
61818781e08Smrg    switch (flip->type) {
61918781e08Smrg    case DRI2_SWAP:
62018781e08Smrg	/* Check for too small vblank count of pageflip completion, taking wraparound
62118781e08Smrg	 * into account. This usually means some defective kms pageflip completion,
62218781e08Smrg	 * causing wrong (msc, ust) return values and possible visual corruption.
62318781e08Smrg	 */
62418781e08Smrg	if ((frame < flip->frame) && (flip->frame - frame < 5)) {
62518781e08Smrg	    xf86DrvMsg(scrn->scrnIndex, X_WARNING,
62618781e08Smrg		       "%s: Pageflip completion event has impossible msc %u < target_msc %u\n",
62718781e08Smrg		       __func__, frame, flip->frame);
62818781e08Smrg	    /* All-Zero values signal failure of (msc, ust) timestamping to client. */
62918781e08Smrg	    frame = tv_sec = tv_usec = 0;
63018781e08Smrg	}
6310d16fef4Smrg
63218781e08Smrg	DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec,
63318781e08Smrg			 DRI2_FLIP_COMPLETE, flip->event_complete,
63418781e08Smrg			 flip->event_data);
63518781e08Smrg	break;
63618781e08Smrg    default:
63718781e08Smrg	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__);
63818781e08Smrg	/* Unknown type */
63918781e08Smrg	break;
6400d16fef4Smrg    }
64118781e08Smrg
64218781e08Smrgabort:
64318781e08Smrg    radeon_dri2_flip_event_abort(crtc, event_data);
644de2362d3Smrg}
645de2362d3Smrg
646de2362d3Smrgstatic Bool
64718781e08Smrgradeon_dri2_schedule_flip(xf86CrtcPtr crtc, ClientPtr client,
648de2362d3Smrg			  DrawablePtr draw, DRI2BufferPtr front,
649de2362d3Smrg			  DRI2BufferPtr back, DRI2SwapEventPtr func,
650de2362d3Smrg			  void *data, unsigned int target_msc)
651de2362d3Smrg{
65218781e08Smrg    ScrnInfoPtr scrn = crtc->scrn;
65318781e08Smrg    RADEONInfoPtr info = RADEONPTR(scrn);
654de2362d3Smrg    struct dri2_buffer_priv *back_priv;
65518781e08Smrg    struct radeon_bo *bo;
656de2362d3Smrg    DRI2FrameEventPtr flip_info;
65718781e08Smrg    int ref_crtc_hw_id = drmmode_get_crtc_id(crtc);
658de2362d3Smrg
659de2362d3Smrg    flip_info = calloc(1, sizeof(DRI2FrameEventRec));
660de2362d3Smrg    if (!flip_info)
661de2362d3Smrg	return FALSE;
662de2362d3Smrg
663de2362d3Smrg    flip_info->drawable_id = draw->id;
664de2362d3Smrg    flip_info->client = client;
665de2362d3Smrg    flip_info->type = DRI2_SWAP;
666de2362d3Smrg    flip_info->event_complete = func;
667de2362d3Smrg    flip_info->event_data = data;
668de2362d3Smrg    flip_info->frame = target_msc;
66918781e08Smrg    flip_info->crtc = crtc;
670de2362d3Smrg
671de2362d3Smrg    xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG,
672de2362d3Smrg		   "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info);
673de2362d3Smrg
674de2362d3Smrg    /* Page flip the full screen buffer */
675de2362d3Smrg    back_priv = back->driverPrivate;
67618781e08Smrg    bo = radeon_get_pixmap_bo(back_priv->pixmap);
67718781e08Smrg
67818781e08Smrg    if (radeon_do_pageflip(scrn, client, bo->handle,
67918781e08Smrg			   RADEON_DRM_QUEUE_ID_DEFAULT, flip_info,
68018781e08Smrg			   ref_crtc_hw_id,
68118781e08Smrg			   radeon_dri2_flip_event_handler,
6823ed65abbSmrg			   radeon_dri2_flip_event_abort, FLIP_VSYNC,
6833ed65abbSmrg			   target_msc - radeon_get_msc_delta(draw, crtc))) {
68418781e08Smrg	info->drmmode.dri2_flipping = TRUE;
68518781e08Smrg	return TRUE;
68618781e08Smrg    }
6870d16fef4Smrg
68818781e08Smrg    return FALSE;
689de2362d3Smrg}
690de2362d3Smrg
691de2362d3Smrgstatic Bool
692de2362d3Smrgupdate_front(DrawablePtr draw, DRI2BufferPtr front)
693de2362d3Smrg{
694de2362d3Smrg    PixmapPtr pixmap;
69518781e08Smrg    RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(draw->pScreen));
696de2362d3Smrg    struct dri2_buffer_priv *priv = front->driverPrivate;
697de2362d3Smrg
69818781e08Smrg    pixmap = get_drawable_pixmap(draw);
699de2362d3Smrg    pixmap->refcnt++;
700de2362d3Smrg
70118781e08Smrg    if (!info->use_glamor)
70218781e08Smrg	exaMoveInPixmap(pixmap);
70318781e08Smrg    if (!radeon_get_flink_name(info, pixmap, &front->name)) {
704de2362d3Smrg	(*draw->pScreen->DestroyPixmap)(pixmap);
705de2362d3Smrg	return FALSE;
706de2362d3Smrg    }
707de2362d3Smrg    (*draw->pScreen->DestroyPixmap)(priv->pixmap);
708de2362d3Smrg    front->pitch = pixmap->devKind;
709de2362d3Smrg    front->cpp = pixmap->drawable.bitsPerPixel / 8;
710de2362d3Smrg    priv->pixmap = pixmap;
711de2362d3Smrg
712de2362d3Smrg    return TRUE;
713de2362d3Smrg}
714de2362d3Smrg
715de2362d3Smrgstatic Bool
716de2362d3Smrgcan_exchange(ScrnInfoPtr pScrn, DrawablePtr draw,
717de2362d3Smrg	     DRI2BufferPtr front, DRI2BufferPtr back)
718de2362d3Smrg{
719de2362d3Smrg    struct dri2_buffer_priv *front_priv = front->driverPrivate;
720de2362d3Smrg    struct dri2_buffer_priv *back_priv = back->driverPrivate;
721de2362d3Smrg    PixmapPtr front_pixmap;
722de2362d3Smrg    PixmapPtr back_pixmap = back_priv->pixmap;
723de2362d3Smrg    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
724de2362d3Smrg    int i;
725de2362d3Smrg
726de2362d3Smrg    for (i = 0; i < xf86_config->num_crtc; i++) {
727de2362d3Smrg	xf86CrtcPtr crtc = xf86_config->crtc[i];
72818781e08Smrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
72918781e08Smrg
73018781e08Smrg	if (crtc->enabled &&
73118781e08Smrg	    (crtc->rotatedData || drmmode_crtc->scanout[0].bo))
732de2362d3Smrg	    return FALSE;
733de2362d3Smrg    }
734de2362d3Smrg
735de2362d3Smrg    if (!update_front(draw, front))
736de2362d3Smrg	return FALSE;
737de2362d3Smrg
738de2362d3Smrg    front_pixmap = front_priv->pixmap;
739de2362d3Smrg
740de2362d3Smrg    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
741de2362d3Smrg	return FALSE;
742de2362d3Smrg
743de2362d3Smrg    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
744de2362d3Smrg	return FALSE;
745de2362d3Smrg
746de2362d3Smrg    if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel)
747de2362d3Smrg	return FALSE;
748de2362d3Smrg
749de2362d3Smrg    if (front_pixmap->devKind != back_pixmap->devKind)
750de2362d3Smrg	return FALSE;
751de2362d3Smrg
752de2362d3Smrg    return TRUE;
753de2362d3Smrg}
754de2362d3Smrg
755de2362d3Smrgstatic Bool
756de2362d3Smrgcan_flip(ScrnInfoPtr pScrn, DrawablePtr draw,
757de2362d3Smrg	 DRI2BufferPtr front, DRI2BufferPtr back)
758de2362d3Smrg{
75918781e08Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
7603ed65abbSmrg    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
7613ed65abbSmrg    int num_crtcs_on;
7623ed65abbSmrg    int i;
7633ed65abbSmrg
7643ed65abbSmrg    if (draw->type != DRAWABLE_WINDOW ||
7653ed65abbSmrg	!info->allowPageFlip ||
7663ed65abbSmrg	info->hwcursor_disabled ||
7673ed65abbSmrg	info->drmmode.present_flipping ||
7683ed65abbSmrg	!pScrn->vtSema ||
7693ed65abbSmrg	!DRI2CanFlip(draw))
7703ed65abbSmrg	return FALSE;
7713ed65abbSmrg
7723ed65abbSmrg    for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) {
7733ed65abbSmrg	xf86CrtcPtr crtc = config->crtc[i];
7743ed65abbSmrg	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
7753ed65abbSmrg
7763ed65abbSmrg	if (!crtc->enabled)
7773ed65abbSmrg	    continue;
7783ed65abbSmrg
7793ed65abbSmrg	if (!drmmode_crtc || drmmode_crtc->rotate.bo ||
7803ed65abbSmrg	    drmmode_crtc->scanout[0].bo)
7813ed65abbSmrg	    return FALSE;
7823ed65abbSmrg
7833ed65abbSmrg	if (drmmode_crtc->pending_dpms_mode == DPMSModeOn)
7843ed65abbSmrg	    num_crtcs_on++;
7853ed65abbSmrg    }
78618781e08Smrg
7873ed65abbSmrg    return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back);
788de2362d3Smrg}
789de2362d3Smrg
790de2362d3Smrgstatic void
791de2362d3Smrgradeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back)
792de2362d3Smrg{
793de2362d3Smrg    struct dri2_buffer_priv *front_priv = front->driverPrivate;
794de2362d3Smrg    struct dri2_buffer_priv *back_priv = back->driverPrivate;
79518781e08Smrg    struct radeon_bo *front_bo, *back_bo;
796de2362d3Smrg    ScreenPtr screen;
797de2362d3Smrg    RADEONInfoPtr info;
79818781e08Smrg    RegionRec region;
799de2362d3Smrg    int tmp;
800de2362d3Smrg
80118781e08Smrg    region.extents.x1 = region.extents.y1 = 0;
80218781e08Smrg    region.extents.x2 = front_priv->pixmap->drawable.width;
80318781e08Smrg    region.extents.y2 = front_priv->pixmap->drawable.height;
80418781e08Smrg    region.data = NULL;
80518781e08Smrg    DamageRegionAppend(&front_priv->pixmap->drawable, &region);
80618781e08Smrg
807de2362d3Smrg    /* Swap BO names so DRI works */
808de2362d3Smrg    tmp = front->name;
809de2362d3Smrg    front->name = back->name;
810de2362d3Smrg    back->name = tmp;
811de2362d3Smrg
812de2362d3Smrg    /* Swap pixmap bos */
81318781e08Smrg    front_bo = radeon_get_pixmap_bo(front_priv->pixmap);
81418781e08Smrg    back_bo = radeon_get_pixmap_bo(back_priv->pixmap);
81518781e08Smrg    radeon_set_pixmap_bo(front_priv->pixmap, back_bo);
81618781e08Smrg    radeon_set_pixmap_bo(back_priv->pixmap, front_bo);
817de2362d3Smrg
818de2362d3Smrg    /* Do we need to update the Screen? */
819de2362d3Smrg    screen = draw->pScreen;
820de2362d3Smrg    info = RADEONPTR(xf86ScreenToScrn(screen));
82118781e08Smrg    if (front_bo == info->front_bo) {
82218781e08Smrg	radeon_bo_ref(back_bo);
823de2362d3Smrg	radeon_bo_unref(info->front_bo);
82418781e08Smrg	info->front_bo = back_bo;
82518781e08Smrg	radeon_set_pixmap_bo(screen->GetScreenPixmap(screen), back_bo);
826de2362d3Smrg    }
82718781e08Smrg
82818781e08Smrg    radeon_glamor_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
82918781e08Smrg
83018781e08Smrg    DamageRegionProcessPending(&front_priv->pixmap->drawable);
831de2362d3Smrg}
832de2362d3Smrg
83318781e08Smrgstatic void radeon_dri2_frame_event_abort(xf86CrtcPtr crtc, void *event_data)
834de2362d3Smrg{
835de2362d3Smrg    DRI2FrameEventPtr event = event_data;
83618781e08Smrg
83718781e08Smrg    TimerCancel(event->timer);
83818781e08Smrg    TimerFree(event->timer);
83918781e08Smrg    radeon_dri2_unref_buffer(event->front);
84018781e08Smrg    radeon_dri2_unref_buffer(event->back);
84118781e08Smrg    free(event);
84218781e08Smrg}
84318781e08Smrg
84418781e08Smrgstatic void radeon_dri2_frame_event_handler(xf86CrtcPtr crtc, uint32_t seq,
84518781e08Smrg					    uint64_t usec, void *event_data)
84618781e08Smrg{
84718781e08Smrg    DRI2FrameEventPtr event = event_data;
84818781e08Smrg    ScrnInfoPtr scrn = crtc->scrn;
849de2362d3Smrg    DrawablePtr drawable;
850de2362d3Smrg    int status;
851de2362d3Smrg    int swap_type;
852de2362d3Smrg    BoxRec box;
853de2362d3Smrg    RegionRec region;
854de2362d3Smrg
855de2362d3Smrg    status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
856de2362d3Smrg                               M_ANY, DixWriteAccess);
857de2362d3Smrg    if (status != Success)
858de2362d3Smrg        goto cleanup;
859de2362d3Smrg
86018781e08Smrg    seq += radeon_get_msc_delta(drawable, crtc);
861de2362d3Smrg
862de2362d3Smrg    switch (event->type) {
863de2362d3Smrg    case DRI2_FLIP:
864de2362d3Smrg	if (can_flip(scrn, drawable, event->front, event->back) &&
86518781e08Smrg	    radeon_dri2_schedule_flip(crtc,
866de2362d3Smrg				      event->client,
867de2362d3Smrg				      drawable,
868de2362d3Smrg				      event->front,
869de2362d3Smrg				      event->back,
870de2362d3Smrg				      event->event_complete,
871de2362d3Smrg				      event->event_data,
872de2362d3Smrg				      event->frame)) {
873de2362d3Smrg	    radeon_dri2_exchange_buffers(drawable, event->front, event->back);
874de2362d3Smrg	    break;
875de2362d3Smrg	}
876de2362d3Smrg	/* else fall through to exchange/blit */
877de2362d3Smrg    case DRI2_SWAP:
878de2362d3Smrg	if (DRI2CanExchange(drawable) &&
879de2362d3Smrg	    can_exchange(scrn, drawable, event->front, event->back)) {
880de2362d3Smrg	    radeon_dri2_exchange_buffers(drawable, event->front, event->back);
881de2362d3Smrg	    swap_type = DRI2_EXCHANGE_COMPLETE;
882de2362d3Smrg	} else {
883de2362d3Smrg	    box.x1 = 0;
884de2362d3Smrg	    box.y1 = 0;
885de2362d3Smrg	    box.x2 = drawable->width;
886de2362d3Smrg	    box.y2 = drawable->height;
887de2362d3Smrg	    REGION_INIT(pScreen, &region, &box, 0);
888de2362d3Smrg	    radeon_dri2_copy_region(drawable, &region, event->front, event->back);
889de2362d3Smrg	    swap_type = DRI2_BLIT_COMPLETE;
890de2362d3Smrg	}
891de2362d3Smrg
89218781e08Smrg        DRI2SwapComplete(event->client, drawable, seq, usec / 1000000,
89318781e08Smrg			 usec % 1000000, swap_type, event->event_complete,
89418781e08Smrg			 event->event_data);
895de2362d3Smrg
896de2362d3Smrg        break;
897de2362d3Smrg    case DRI2_WAITMSC:
89818781e08Smrg        DRI2WaitMSCComplete(event->client, drawable, seq, usec / 1000000,
89918781e08Smrg			    usec % 1000000);
900de2362d3Smrg        break;
901de2362d3Smrg    default:
902de2362d3Smrg        /* Unknown type */
903de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
904de2362d3Smrg                "%s: unknown vblank event received\n", __func__);
905de2362d3Smrg        break;
906de2362d3Smrg    }
907de2362d3Smrg
908de2362d3Smrgcleanup:
90918781e08Smrg    radeon_dri2_frame_event_abort(crtc, event_data);
910de2362d3Smrg}
911de2362d3Smrg
91218781e08SmrgdrmVBlankSeqType radeon_populate_vbl_request_type(xf86CrtcPtr crtc)
913de2362d3Smrg{
914de2362d3Smrg    drmVBlankSeqType type = 0;
91518781e08Smrg    int crtc_id = drmmode_get_crtc_id(crtc);
916de2362d3Smrg
91718781e08Smrg    if (crtc_id == 1)
918de2362d3Smrg        type |= DRM_VBLANK_SECONDARY;
91918781e08Smrg    else if (crtc_id > 1)
920de2362d3Smrg#ifdef DRM_VBLANK_HIGH_CRTC_SHIFT
92118781e08Smrg	type |= (crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT) &
922de2362d3Smrg		DRM_VBLANK_HIGH_CRTC_MASK;
923de2362d3Smrg#else
924de2362d3Smrg	ErrorF("radeon driver bug: %s called for CRTC %d > 1, but "
925de2362d3Smrg	       "DRM_VBLANK_HIGH_CRTC_MASK not defined at build time\n",
92618781e08Smrg	       __func__, crtc_id);
927de2362d3Smrg#endif
928de2362d3Smrg
929de2362d3Smrg    return type;
930de2362d3Smrg}
931de2362d3Smrg
932de2362d3Smrg/*
93318781e08Smrg * This function should be called on a disabled CRTC only (i.e., CRTC
93418781e08Smrg * in DPMS-off state). It will calculate the delay necessary to reach
93518781e08Smrg * target_msc from present time if the CRTC were running.
936de2362d3Smrg */
93718781e08Smrgstatic
93818781e08SmrgCARD32 radeon_dri2_extrapolate_msc_delay(xf86CrtcPtr crtc, CARD64 *target_msc,
93918781e08Smrg					 CARD64 divisor, CARD64 remainder)
940de2362d3Smrg{
94118781e08Smrg    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
94218781e08Smrg    ScrnInfoPtr pScrn = crtc->scrn;
94318781e08Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
94418781e08Smrg    int nominal_frame_rate = drmmode_crtc->dpms_last_fps;
94518781e08Smrg    CARD64 last_vblank_ust = drmmode_crtc->dpms_last_ust;
94618781e08Smrg    uint32_t last_vblank_seq = drmmode_crtc->dpms_last_seq;
94718781e08Smrg    CARD64 now, target_time, delta_t;
94818781e08Smrg    int64_t d, delta_seq;
9497821949aSmrg    int ret;
95018781e08Smrg    CARD32 d_ms;
95118781e08Smrg
95218781e08Smrg    if (!last_vblank_ust) {
95318781e08Smrg	*target_msc = 0;
95418781e08Smrg	return FALLBACK_SWAP_DELAY;
95518781e08Smrg    }
95618781e08Smrg    ret = drmmode_get_current_ust(info->dri2.drm_fd, &now);
95718781e08Smrg    if (ret) {
95818781e08Smrg	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
95918781e08Smrg		   "%s cannot get current time\n", __func__);
96018781e08Smrg	*target_msc = 0;
96118781e08Smrg	return FALLBACK_SWAP_DELAY;
96218781e08Smrg    }
96318781e08Smrg    delta_seq = *target_msc - last_vblank_seq;
96418781e08Smrg    delta_seq *= 1000000;
96518781e08Smrg    target_time = last_vblank_ust;
96618781e08Smrg    target_time += delta_seq / nominal_frame_rate;
96718781e08Smrg    d = target_time - now;
96818781e08Smrg    if (d < 0) {
96918781e08Smrg	/* we missed the event, adjust target_msc, do the divisor magic */
97018781e08Smrg	CARD64 current_msc = last_vblank_seq;
97118781e08Smrg
97218781e08Smrg	delta_t = now - last_vblank_ust;
97318781e08Smrg	delta_seq = delta_t * nominal_frame_rate;
97418781e08Smrg	current_msc += delta_seq / 1000000;
97518781e08Smrg	current_msc &= 0xffffffff;
97618781e08Smrg	if (divisor == 0) {
97718781e08Smrg	    *target_msc = current_msc;
97818781e08Smrg	    d = 0;
97918781e08Smrg	} else {
98018781e08Smrg	    *target_msc = current_msc - (current_msc % divisor) + remainder;
98118781e08Smrg	    if ((current_msc % divisor) >= remainder)
98218781e08Smrg		*target_msc += divisor;
98318781e08Smrg	    *target_msc &= 0xffffffff;
98418781e08Smrg	    delta_seq = *target_msc - last_vblank_seq;
98518781e08Smrg	    delta_seq *= 1000000;
98618781e08Smrg	    target_time = last_vblank_ust;
98718781e08Smrg	    target_time += delta_seq / nominal_frame_rate;
98818781e08Smrg	    d = target_time - now;
98918781e08Smrg	}
99018781e08Smrg    }
99118781e08Smrg    /*
99218781e08Smrg     * convert delay to milliseconds and add margin to prevent the client
99318781e08Smrg     * from coming back early (due to timer granularity and rounding
99418781e08Smrg     * errors) and getting the same MSC it just got
99518781e08Smrg     */
99618781e08Smrg    d_ms = (CARD32)d / 1000;
99718781e08Smrg    if ((CARD32)d - d_ms * 1000 > 0)
99818781e08Smrg	d_ms += 2;
99918781e08Smrg    else
100018781e08Smrg	d_ms++;
100118781e08Smrg    return d_ms;
100218781e08Smrg}
100318781e08Smrg
100418781e08Smrg/*
100518781e08Smrg * Get current interpolated frame count and frame count timestamp, based on
100618781e08Smrg * drawable's crtc.
100718781e08Smrg */
100818781e08Smrgstatic int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc)
100918781e08Smrg{
101018781e08Smrg    xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw, TRUE);
1011de2362d3Smrg
1012de2362d3Smrg    /* Drawable not displayed, make up a value */
101318781e08Smrg    if (crtc == NULL) {
1014de2362d3Smrg        *ust = 0;
1015de2362d3Smrg        *msc = 0;
1016de2362d3Smrg        return TRUE;
1017de2362d3Smrg    }
1018de2362d3Smrg
101918781e08Smrg    if (!radeon_dri2_get_crtc_msc(crtc, ust, msc))
102018781e08Smrg	return FALSE;
102118781e08Smrg
102218781e08Smrg    if (draw && draw->type == DRAWABLE_WINDOW)
102318781e08Smrg	*msc += get_dri2_window_priv((WindowPtr)draw)->vblank_delta;
102418781e08Smrg    *msc &= 0xffffffff;
102518781e08Smrg    return TRUE;
102618781e08Smrg}
102718781e08Smrg
102818781e08Smrgstatic
102918781e08SmrgCARD32 radeon_dri2_deferred_event(OsTimerPtr timer, CARD32 now, pointer data)
103018781e08Smrg{
103118781e08Smrg    DRI2FrameEventPtr event_info = (DRI2FrameEventPtr)data;
103218781e08Smrg    xf86CrtcPtr crtc = event_info->crtc;
103318781e08Smrg    ScrnInfoPtr scrn;
103418781e08Smrg    RADEONInfoPtr info;
103518781e08Smrg    CARD64 drm_now;
103618781e08Smrg    int ret;
103718781e08Smrg    CARD64 delta_t, delta_seq, frame;
103818781e08Smrg    drmmode_crtc_private_ptr drmmode_crtc;
103918781e08Smrg
104018781e08Smrg    /*
104118781e08Smrg     * This is emulated event, so its time is current time, which we
104218781e08Smrg     * have to get in DRM-compatible form (which is a bit messy given
104318781e08Smrg     * the information that we have at this point). Can't use now argument
104418781e08Smrg     * because DRM event time may come from monotonic clock, while
104518781e08Smrg     * DIX timer facility uses real-time clock.
104618781e08Smrg     */
104718781e08Smrg    if (!event_info->crtc) {
104818781e08Smrg	ErrorF("%s no crtc\n", __func__);
104918781e08Smrg	if (event_info->drm_queue_seq)
105018781e08Smrg	    radeon_drm_abort_entry(event_info->drm_queue_seq);
105118781e08Smrg	else
105218781e08Smrg	    radeon_dri2_frame_event_abort(NULL, data);
105318781e08Smrg	return 0;
1054de2362d3Smrg    }
1055de2362d3Smrg
105618781e08Smrg    scrn = crtc->scrn;
105718781e08Smrg    info = RADEONPTR(scrn);
105818781e08Smrg    ret = drmmode_get_current_ust(info->dri2.drm_fd, &drm_now);
105918781e08Smrg    if (ret) {
106018781e08Smrg	xf86DrvMsg(scrn->scrnIndex, X_ERROR,
106118781e08Smrg		   "%s cannot get current time\n", __func__);
106218781e08Smrg	if (event_info->drm_queue_seq)
106318781e08Smrg	    radeon_drm_queue_handler(info->dri2.drm_fd, 0, 0, 0,
106418781e08Smrg				     (void*)event_info->drm_queue_seq);
106518781e08Smrg	else
106618781e08Smrg	    radeon_dri2_frame_event_handler(crtc, 0, 0, data);
106718781e08Smrg	return 0;
106818781e08Smrg    }
106918781e08Smrg    /*
107018781e08Smrg     * calculate the frame number from current time
107118781e08Smrg     * that would come from CRTC if it were running
107218781e08Smrg     */
107318781e08Smrg    drmmode_crtc = event_info->crtc->driver_private;
107418781e08Smrg    delta_t = drm_now - (CARD64)drmmode_crtc->dpms_last_ust;
107518781e08Smrg    delta_seq = delta_t * drmmode_crtc->dpms_last_fps;
107618781e08Smrg    delta_seq /= 1000000;
107718781e08Smrg    frame = (CARD64)drmmode_crtc->dpms_last_seq + delta_seq;
107818781e08Smrg    if (event_info->drm_queue_seq)
107918781e08Smrg	radeon_drm_queue_handler(info->dri2.drm_fd, frame, drm_now / 1000000,
108018781e08Smrg				 drm_now % 1000000,
108118781e08Smrg				 (void*)event_info->drm_queue_seq);
108218781e08Smrg    else
108318781e08Smrg	radeon_dri2_frame_event_handler(crtc, frame, drm_now, data);
108418781e08Smrg    return 0;
108518781e08Smrg}
10867821949aSmrg
108718781e08Smrgstatic
108818781e08Smrgvoid radeon_dri2_schedule_event(CARD32 delay, DRI2FrameEventPtr event_info)
108918781e08Smrg{
109018781e08Smrg    event_info->timer = TimerSet(NULL, 0, delay, radeon_dri2_deferred_event,
109118781e08Smrg				 event_info);
109218781e08Smrg    if (delay == 0) {
109318781e08Smrg	CARD32 now = GetTimeInMillis();
109418781e08Smrg	radeon_dri2_deferred_event(event_info->timer, now, event_info);
109518781e08Smrg    }
1096de2362d3Smrg}
1097de2362d3Smrg
1098de2362d3Smrg/*
1099de2362d3Smrg * Request a DRM event when the requested conditions will be satisfied.
1100de2362d3Smrg *
1101de2362d3Smrg * We need to handle the event and ask the server to wake up the client when
1102de2362d3Smrg * we receive it.
1103de2362d3Smrg */
1104de2362d3Smrgstatic int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
1105de2362d3Smrg                                         CARD64 target_msc, CARD64 divisor,
1106de2362d3Smrg                                         CARD64 remainder)
1107de2362d3Smrg{
1108de2362d3Smrg    ScreenPtr screen = draw->pScreen;
1109de2362d3Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
1110de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(scrn);
1111de2362d3Smrg    DRI2FrameEventPtr wait_info = NULL;
111218781e08Smrg    uintptr_t drm_queue_seq = 0;
111318781e08Smrg    xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw, TRUE);
111418781e08Smrg    uint32_t msc_delta;
1115de2362d3Smrg    drmVBlank vbl;
111618781e08Smrg    int ret;
1117de2362d3Smrg    CARD64 current_msc;
1118de2362d3Smrg
1119de2362d3Smrg    /* Truncate to match kernel interfaces; means occasional overflow
1120de2362d3Smrg     * misses, but that's generally not a big deal */
1121de2362d3Smrg    target_msc &= 0xffffffff;
1122de2362d3Smrg    divisor &= 0xffffffff;
1123de2362d3Smrg    remainder &= 0xffffffff;
1124de2362d3Smrg
1125de2362d3Smrg    /* Drawable not visible, return immediately */
112618781e08Smrg    if (crtc == NULL)
1127de2362d3Smrg        goto out_complete;
1128de2362d3Smrg
112918781e08Smrg    msc_delta = radeon_get_msc_delta(draw, crtc);
113018781e08Smrg
1131de2362d3Smrg    wait_info = calloc(1, sizeof(DRI2FrameEventRec));
1132de2362d3Smrg    if (!wait_info)
1133de2362d3Smrg        goto out_complete;
1134de2362d3Smrg
1135de2362d3Smrg    wait_info->drawable_id = draw->id;
1136de2362d3Smrg    wait_info->client = client;
1137de2362d3Smrg    wait_info->type = DRI2_WAITMSC;
113818781e08Smrg    wait_info->crtc = crtc;
1139de2362d3Smrg
114018781e08Smrg    /*
114118781e08Smrg     * CRTC is in DPMS off state, calculate wait time from current time,
114218781e08Smrg     * target_msc and last vblank time/sequence when CRTC was turned off
114318781e08Smrg     */
114418781e08Smrg    if (!radeon_crtc_is_enabled(crtc)) {
114518781e08Smrg	CARD32 delay;
114618781e08Smrg	target_msc -= msc_delta;
114718781e08Smrg	delay = radeon_dri2_extrapolate_msc_delay(crtc, &target_msc,
114818781e08Smrg						  divisor, remainder);
114918781e08Smrg	radeon_dri2_schedule_event(delay, wait_info);
115018781e08Smrg	DRI2BlockClient(client, draw);
115118781e08Smrg	return TRUE;
1152de2362d3Smrg    }
1153de2362d3Smrg
1154de2362d3Smrg    /* Get current count */
1155de2362d3Smrg    vbl.request.type = DRM_VBLANK_RELATIVE;
115618781e08Smrg    vbl.request.type |= radeon_populate_vbl_request_type(crtc);
1157de2362d3Smrg    vbl.request.sequence = 0;
1158de2362d3Smrg    ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1159de2362d3Smrg    if (ret) {
1160de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1161de2362d3Smrg                "get vblank counter failed: %s\n", strerror(errno));
1162de2362d3Smrg        goto out_complete;
1163de2362d3Smrg    }
1164de2362d3Smrg
116518781e08Smrg    current_msc = vbl.reply.sequence + msc_delta;
116618781e08Smrg    current_msc &= 0xffffffff;
116718781e08Smrg
116818781e08Smrg    drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
116918781e08Smrg					   wait_info, radeon_dri2_frame_event_handler,
117018781e08Smrg					   radeon_dri2_frame_event_abort);
117118781e08Smrg    if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
117218781e08Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
117318781e08Smrg		   "Allocating DRM queue event entry failed.\n");
117418781e08Smrg        goto out_complete;
117518781e08Smrg    }
117618781e08Smrg    wait_info->drm_queue_seq = drm_queue_seq;
11770d16fef4Smrg
1178de2362d3Smrg    /*
1179de2362d3Smrg     * If divisor is zero, or current_msc is smaller than target_msc,
1180de2362d3Smrg     * we just need to make sure target_msc passes  before waking up the
1181de2362d3Smrg     * client.
1182de2362d3Smrg     */
1183de2362d3Smrg    if (divisor == 0 || current_msc < target_msc) {
1184de2362d3Smrg        /* If target_msc already reached or passed, set it to
1185de2362d3Smrg         * current_msc to ensure we return a reasonable value back
1186de2362d3Smrg         * to the caller. This keeps the client from continually
1187de2362d3Smrg         * sending us MSC targets from the past by forcibly updating
1188de2362d3Smrg         * their count on this call.
1189de2362d3Smrg         */
1190de2362d3Smrg        if (current_msc >= target_msc)
1191de2362d3Smrg            target_msc = current_msc;
1192de2362d3Smrg        vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
119318781e08Smrg	vbl.request.type |= radeon_populate_vbl_request_type(crtc);
119418781e08Smrg        vbl.request.sequence = target_msc - msc_delta;
119518781e08Smrg        vbl.request.signal = drm_queue_seq;
1196de2362d3Smrg        ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1197de2362d3Smrg        if (ret) {
1198de2362d3Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1199de2362d3Smrg                    "get vblank counter failed: %s\n", strerror(errno));
1200de2362d3Smrg            goto out_complete;
1201de2362d3Smrg        }
1202de2362d3Smrg
1203de2362d3Smrg        DRI2BlockClient(client, draw);
1204de2362d3Smrg        return TRUE;
1205de2362d3Smrg    }
1206de2362d3Smrg
1207de2362d3Smrg    /*
1208de2362d3Smrg     * If we get here, target_msc has already passed or we don't have one,
1209de2362d3Smrg     * so we queue an event that will satisfy the divisor/remainder equation.
1210de2362d3Smrg     */
1211de2362d3Smrg    vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
121218781e08Smrg    vbl.request.type |= radeon_populate_vbl_request_type(crtc);
1213de2362d3Smrg
1214de2362d3Smrg    vbl.request.sequence = current_msc - (current_msc % divisor) +
121518781e08Smrg        remainder - msc_delta;
1216de2362d3Smrg
1217de2362d3Smrg    /*
1218de2362d3Smrg     * If calculated remainder is larger than requested remainder,
1219de2362d3Smrg     * it means we've passed the last point where
1220de2362d3Smrg     * seq % divisor == remainder, so we need to wait for the next time
1221de2362d3Smrg     * that will happen.
1222de2362d3Smrg     */
1223de2362d3Smrg    if ((current_msc % divisor) >= remainder)
1224de2362d3Smrg        vbl.request.sequence += divisor;
1225de2362d3Smrg
122618781e08Smrg    vbl.request.signal = drm_queue_seq;
1227de2362d3Smrg    ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1228de2362d3Smrg    if (ret) {
1229de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1230de2362d3Smrg                "get vblank counter failed: %s\n", strerror(errno));
1231de2362d3Smrg        goto out_complete;
1232de2362d3Smrg    }
1233de2362d3Smrg
1234de2362d3Smrg    DRI2BlockClient(client, draw);
1235de2362d3Smrg
1236de2362d3Smrg    return TRUE;
1237de2362d3Smrg
1238de2362d3Smrgout_complete:
123918781e08Smrg    if (wait_info)
124018781e08Smrg	radeon_dri2_deferred_event(NULL, 0, wait_info);
1241de2362d3Smrg    return TRUE;
1242de2362d3Smrg}
1243de2362d3Smrg
1244de2362d3Smrg/*
1245de2362d3Smrg * ScheduleSwap is responsible for requesting a DRM vblank event for the
1246de2362d3Smrg * appropriate frame.
1247de2362d3Smrg *
1248de2362d3Smrg * In the case of a blit (e.g. for a windowed swap) or buffer exchange,
1249de2362d3Smrg * the vblank requested can simply be the last queued swap frame + the swap
1250de2362d3Smrg * interval for the drawable.
1251de2362d3Smrg *
1252de2362d3Smrg * In the case of a page flip, we request an event for the last queued swap
1253de2362d3Smrg * frame + swap interval - 1, since we'll need to queue the flip for the frame
1254de2362d3Smrg * immediately following the received event.
1255de2362d3Smrg *
1256de2362d3Smrg * The client will be blocked if it tries to perform further GL commands
1257de2362d3Smrg * after queueing a swap, though in the Intel case after queueing a flip, the
1258de2362d3Smrg * client is free to queue more commands; they'll block in the kernel if
1259de2362d3Smrg * they access buffers busy with the flip.
1260de2362d3Smrg *
1261de2362d3Smrg * When the swap is complete, the driver should call into the server so it
1262de2362d3Smrg * can send any swap complete events that have been requested.
1263de2362d3Smrg */
1264de2362d3Smrgstatic int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
1265de2362d3Smrg                                     DRI2BufferPtr front, DRI2BufferPtr back,
1266de2362d3Smrg                                     CARD64 *target_msc, CARD64 divisor,
1267de2362d3Smrg                                     CARD64 remainder, DRI2SwapEventPtr func,
1268de2362d3Smrg                                     void *data)
1269de2362d3Smrg{
1270de2362d3Smrg    ScreenPtr screen = draw->pScreen;
1271de2362d3Smrg    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
1272de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(scrn);
127318781e08Smrg    xf86CrtcPtr crtc = radeon_dri2_drawable_crtc(draw, TRUE);
127418781e08Smrg    uint32_t msc_delta;
1275de2362d3Smrg    drmVBlank vbl;
127618781e08Smrg    int ret, flip = 0;
1277de2362d3Smrg    DRI2FrameEventPtr swap_info = NULL;
127818781e08Smrg    uintptr_t drm_queue_seq;
1279de2362d3Smrg    CARD64 current_msc;
1280de2362d3Smrg    BoxRec box;
1281de2362d3Smrg    RegionRec region;
1282de2362d3Smrg
1283de2362d3Smrg    /* Truncate to match kernel interfaces; means occasional overflow
1284de2362d3Smrg     * misses, but that's generally not a big deal */
1285de2362d3Smrg    *target_msc &= 0xffffffff;
1286de2362d3Smrg    divisor &= 0xffffffff;
1287de2362d3Smrg    remainder &= 0xffffffff;
1288de2362d3Smrg
1289de2362d3Smrg    /* radeon_dri2_frame_event_handler will get called some unknown time in the
1290de2362d3Smrg     * future with these buffers.  Take a reference to ensure that they won't
1291de2362d3Smrg     * get destroyed before then.
1292de2362d3Smrg     */
1293de2362d3Smrg    radeon_dri2_ref_buffer(front);
1294de2362d3Smrg    radeon_dri2_ref_buffer(back);
1295de2362d3Smrg
129618781e08Smrg    /* either off-screen or CRTC not usable... just complete the swap */
129718781e08Smrg    if (crtc == NULL)
1298de2362d3Smrg        goto blit_fallback;
1299de2362d3Smrg
130018781e08Smrg    msc_delta = radeon_get_msc_delta(draw, crtc);
130118781e08Smrg
1302de2362d3Smrg    swap_info = calloc(1, sizeof(DRI2FrameEventRec));
1303de2362d3Smrg    if (!swap_info)
1304de2362d3Smrg        goto blit_fallback;
1305de2362d3Smrg
130618781e08Smrg    swap_info->type = DRI2_SWAP;
1307de2362d3Smrg    swap_info->drawable_id = draw->id;
1308de2362d3Smrg    swap_info->client = client;
1309de2362d3Smrg    swap_info->event_complete = func;
1310de2362d3Smrg    swap_info->event_data = data;
1311de2362d3Smrg    swap_info->front = front;
1312de2362d3Smrg    swap_info->back = back;
131318781e08Smrg    swap_info->crtc = crtc;
131418781e08Smrg
131518781e08Smrg    drm_queue_seq = radeon_drm_queue_alloc(crtc, client, RADEON_DRM_QUEUE_ID_DEFAULT,
131618781e08Smrg					   swap_info, radeon_dri2_frame_event_handler,
131718781e08Smrg					   radeon_dri2_frame_event_abort);
131818781e08Smrg    if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
1319de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
132018781e08Smrg		   "Allocating DRM queue entry failed.\n");
1321de2362d3Smrg        goto blit_fallback;
1322de2362d3Smrg    }
132318781e08Smrg    swap_info->drm_queue_seq = drm_queue_seq;
132418781e08Smrg
132518781e08Smrg    /*
132618781e08Smrg     * CRTC is in DPMS off state, fallback to blit, but calculate
132718781e08Smrg     * wait time from current time, target_msc and last vblank
132818781e08Smrg     * time/sequence when CRTC was turned off
132918781e08Smrg     */
133018781e08Smrg    if (!radeon_crtc_is_enabled(crtc)) {
133118781e08Smrg	CARD32 delay;
133218781e08Smrg	*target_msc -= msc_delta;
133318781e08Smrg	delay = radeon_dri2_extrapolate_msc_delay(crtc, target_msc,
133418781e08Smrg						  divisor, remainder);
133518781e08Smrg	*target_msc += msc_delta;
133618781e08Smrg	*target_msc &= 0xffffffff;
133718781e08Smrg	radeon_dri2_schedule_event(delay, swap_info);
133818781e08Smrg	return TRUE;
133918781e08Smrg    }
1340de2362d3Smrg
1341de2362d3Smrg    /* Get current count */
1342de2362d3Smrg    vbl.request.type = DRM_VBLANK_RELATIVE;
134318781e08Smrg    vbl.request.type |= radeon_populate_vbl_request_type(crtc);
1344de2362d3Smrg    vbl.request.sequence = 0;
1345de2362d3Smrg    ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1346de2362d3Smrg    if (ret) {
1347de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1348de2362d3Smrg                "first get vblank counter failed: %s\n",
1349de2362d3Smrg                strerror(errno));
135018781e08Smrg	goto blit_fallback;
1351de2362d3Smrg    }
1352de2362d3Smrg
135318781e08Smrg    current_msc = vbl.reply.sequence + msc_delta;
135418781e08Smrg    current_msc &= 0xffffffff;
1355de2362d3Smrg
1356de2362d3Smrg    /* Flips need to be submitted one frame before */
1357de2362d3Smrg    if (can_flip(scrn, draw, front, back)) {
135818781e08Smrg	swap_info->type = DRI2_FLIP;
1359de2362d3Smrg	flip = 1;
1360de2362d3Smrg    }
1361de2362d3Smrg
136218781e08Smrg    /* Correct target_msc by 'flip' if swap_info->type == DRI2_FLIP.
1363de2362d3Smrg     * Do it early, so handling of different timing constraints
1364de2362d3Smrg     * for divisor, remainder and msc vs. target_msc works.
1365de2362d3Smrg     */
1366de2362d3Smrg    if (*target_msc > 0)
1367de2362d3Smrg        *target_msc -= flip;
1368de2362d3Smrg
1369de2362d3Smrg    /*
1370de2362d3Smrg     * If divisor is zero, or current_msc is smaller than target_msc
1371de2362d3Smrg     * we just need to make sure target_msc passes before initiating
1372de2362d3Smrg     * the swap.
1373de2362d3Smrg     */
1374de2362d3Smrg    if (divisor == 0 || current_msc < *target_msc) {
1375de2362d3Smrg        vbl.request.type =  DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
1376de2362d3Smrg        /* If non-pageflipping, but blitting/exchanging, we need to use
1377de2362d3Smrg         * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
1378de2362d3Smrg         * on.
1379de2362d3Smrg         */
1380de2362d3Smrg        if (flip == 0)
1381de2362d3Smrg            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
138218781e08Smrg	vbl.request.type |= radeon_populate_vbl_request_type(crtc);
1383de2362d3Smrg
1384de2362d3Smrg        /* If target_msc already reached or passed, set it to
1385de2362d3Smrg         * current_msc to ensure we return a reasonable value back
1386de2362d3Smrg         * to the caller. This makes swap_interval logic more robust.
1387de2362d3Smrg         */
1388de2362d3Smrg        if (current_msc >= *target_msc)
1389de2362d3Smrg            *target_msc = current_msc;
1390de2362d3Smrg
139118781e08Smrg        vbl.request.sequence = *target_msc - msc_delta;
139218781e08Smrg        vbl.request.signal = drm_queue_seq;
1393de2362d3Smrg        ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1394de2362d3Smrg        if (ret) {
1395de2362d3Smrg            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1396de2362d3Smrg                    "divisor 0 get vblank counter failed: %s\n",
1397de2362d3Smrg                    strerror(errno));
139818781e08Smrg	    goto blit_fallback;
1399de2362d3Smrg        }
1400de2362d3Smrg
140118781e08Smrg        *target_msc = vbl.reply.sequence + flip + msc_delta;
1402de2362d3Smrg        swap_info->frame = *target_msc;
1403de2362d3Smrg
1404de2362d3Smrg        return TRUE;
1405de2362d3Smrg    }
1406de2362d3Smrg
1407de2362d3Smrg    /*
1408de2362d3Smrg     * If we get here, target_msc has already passed or we don't have one,
1409de2362d3Smrg     * and we need to queue an event that will satisfy the divisor/remainder
1410de2362d3Smrg     * equation.
1411de2362d3Smrg     */
1412de2362d3Smrg    vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
1413de2362d3Smrg    if (flip == 0)
1414de2362d3Smrg        vbl.request.type |= DRM_VBLANK_NEXTONMISS;
141518781e08Smrg    vbl.request.type |= radeon_populate_vbl_request_type(crtc);
1416de2362d3Smrg
1417de2362d3Smrg    vbl.request.sequence = current_msc - (current_msc % divisor) +
141818781e08Smrg        remainder - msc_delta;
1419de2362d3Smrg
1420de2362d3Smrg    /*
1421de2362d3Smrg     * If the calculated deadline vbl.request.sequence is smaller than
1422de2362d3Smrg     * or equal to current_msc, it means we've passed the last point
1423de2362d3Smrg     * when effective onset frame seq could satisfy
1424de2362d3Smrg     * seq % divisor == remainder, so we need to wait for the next time
1425de2362d3Smrg     * this will happen.
1426de2362d3Smrg
1427de2362d3Smrg     * This comparison takes the 1 frame swap delay in pageflipping mode
1428de2362d3Smrg     * into account, as well as a potential DRM_VBLANK_NEXTONMISS delay
1429de2362d3Smrg     * if we are blitting/exchanging instead of flipping.
1430de2362d3Smrg     */
1431de2362d3Smrg    if (vbl.request.sequence <= current_msc)
1432de2362d3Smrg        vbl.request.sequence += divisor;
1433de2362d3Smrg
1434de2362d3Smrg    /* Account for 1 frame extra pageflip delay if flip > 0 */
1435de2362d3Smrg    vbl.request.sequence -= flip;
1436de2362d3Smrg
143718781e08Smrg    vbl.request.signal = drm_queue_seq;
1438de2362d3Smrg    ret = drmWaitVBlank(info->dri2.drm_fd, &vbl);
1439de2362d3Smrg    if (ret) {
1440de2362d3Smrg        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1441de2362d3Smrg                "final get vblank counter failed: %s\n",
1442de2362d3Smrg                strerror(errno));
144318781e08Smrg	goto blit_fallback;
1444de2362d3Smrg    }
1445de2362d3Smrg
1446de2362d3Smrg    /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
144718781e08Smrg    *target_msc = vbl.reply.sequence + flip + msc_delta;
144818781e08Smrg    *target_msc &= 0xffffffff;
1449de2362d3Smrg    swap_info->frame = *target_msc;
1450de2362d3Smrg
1451de2362d3Smrg    return TRUE;
1452de2362d3Smrg
1453de2362d3Smrgblit_fallback:
145418781e08Smrg    if (swap_info) {
145518781e08Smrg	swap_info->type = DRI2_SWAP;
145618781e08Smrg	radeon_dri2_schedule_event(FALLBACK_SWAP_DELAY, swap_info);
145718781e08Smrg    } else {
145818781e08Smrg	box.x1 = 0;
145918781e08Smrg	box.y1 = 0;
146018781e08Smrg	box.x2 = draw->width;
146118781e08Smrg	box.y2 = draw->height;
146218781e08Smrg	REGION_INIT(pScreen, &region, &box, 0);
1463de2362d3Smrg
146418781e08Smrg	radeon_dri2_copy_region(draw, &region, front, back);
1465de2362d3Smrg
146618781e08Smrg	DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
1467de2362d3Smrg
146818781e08Smrg	radeon_dri2_unref_buffer(front);
146918781e08Smrg	radeon_dri2_unref_buffer(back);
147018781e08Smrg    }
14717821949aSmrg
1472de2362d3Smrg    *target_msc = 0; /* offscreen, so zero out target vblank count */
1473de2362d3Smrg    return TRUE;
1474de2362d3Smrg}
1475de2362d3Smrg
1476de2362d3Smrg
1477de2362d3SmrgBool
1478de2362d3Smrgradeon_dri2_screen_init(ScreenPtr pScreen)
1479de2362d3Smrg{
1480de2362d3Smrg    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1481de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
1482de2362d3Smrg    DRI2InfoRec dri2_info = { 0 };
1483de2362d3Smrg    const char *driverNames[2];
1484de2362d3Smrg    Bool scheduling_works = TRUE;
1485de2362d3Smrg
148618781e08Smrg    if (!info->dri2.available)
1487de2362d3Smrg        return FALSE;
1488de2362d3Smrg
1489de2362d3Smrg    info->dri2.device_name = drmGetDeviceNameFromFd(info->dri2.drm_fd);
1490de2362d3Smrg
149118781e08Smrg    if ( (info->ChipFamily >= CHIP_FAMILY_TAHITI) ) {
149218781e08Smrg        dri2_info.driverName = SI_DRIVER_NAME;
149318781e08Smrg    } else if ( (info->ChipFamily >= CHIP_FAMILY_R600) ) {
1494de2362d3Smrg        dri2_info.driverName = R600_DRIVER_NAME;
1495de2362d3Smrg    } else if ( (info->ChipFamily >= CHIP_FAMILY_R300) ) {
1496de2362d3Smrg        dri2_info.driverName = R300_DRIVER_NAME;
1497de2362d3Smrg    } else if ( info->ChipFamily >= CHIP_FAMILY_R200 ) {
1498de2362d3Smrg        dri2_info.driverName = R200_DRIVER_NAME;
1499de2362d3Smrg    } else {
1500de2362d3Smrg        dri2_info.driverName = RADEON_DRIVER_NAME;
1501de2362d3Smrg    }
1502de2362d3Smrg    dri2_info.fd = info->dri2.drm_fd;
1503de2362d3Smrg    dri2_info.deviceName = info->dri2.device_name;
1504de2362d3Smrg    dri2_info.version = DRI2INFOREC_VERSION;
1505de2362d3Smrg    dri2_info.CreateBuffer = radeon_dri2_create_buffer;
1506de2362d3Smrg    dri2_info.DestroyBuffer = radeon_dri2_destroy_buffer;
1507de2362d3Smrg    dri2_info.CopyRegion = radeon_dri2_copy_region;
1508de2362d3Smrg
150918781e08Smrg    if (info->dri2.pKernelDRMVersion->version_minor < 4) {
1510de2362d3Smrg	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for "
1511de2362d3Smrg		   "sync extension\n");
1512de2362d3Smrg	scheduling_works = FALSE;
1513de2362d3Smrg    }
1514de2362d3Smrg
151518781e08Smrg    if (scheduling_works && info->drmmode.count_crtcs > 2) {
1516de2362d3Smrg#ifdef DRM_CAP_VBLANK_HIGH_CRTC
1517de2362d3Smrg	uint64_t cap_value;
1518de2362d3Smrg
1519de2362d3Smrg	if (drmGetCap(info->dri2.drm_fd, DRM_CAP_VBLANK_HIGH_CRTC, &cap_value)) {
1520de2362d3Smrg	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel "
1521de2362d3Smrg		       "for VBLANKs on CRTC > 1\n");
1522de2362d3Smrg	    scheduling_works = FALSE;
1523de2362d3Smrg	} else if (!cap_value) {
1524de2362d3Smrg	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Your kernel does not "
1525de2362d3Smrg		       "handle VBLANKs on CRTC > 1\n");
1526de2362d3Smrg	    scheduling_works = FALSE;
1527de2362d3Smrg	}
1528de2362d3Smrg#else
1529de2362d3Smrg	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need to rebuild against a "
1530de2362d3Smrg		   "newer libdrm to handle VBLANKs on CRTC > 1\n");
1531de2362d3Smrg	scheduling_works = FALSE;
1532de2362d3Smrg#endif
1533de2362d3Smrg    }
1534de2362d3Smrg
1535de2362d3Smrg    if (scheduling_works) {
1536de2362d3Smrg        dri2_info.version = 4;
1537de2362d3Smrg        dri2_info.ScheduleSwap = radeon_dri2_schedule_swap;
1538de2362d3Smrg        dri2_info.GetMSC = radeon_dri2_get_msc;
1539de2362d3Smrg        dri2_info.ScheduleWaitMSC = radeon_dri2_schedule_wait_msc;
1540de2362d3Smrg        dri2_info.numDrivers = RADEON_ARRAY_SIZE(driverNames);
1541de2362d3Smrg        dri2_info.driverNames = driverNames;
154218781e08Smrg        driverNames[0] = dri2_info.driverName;
154318781e08Smrg
154418781e08Smrg        if (info->ChipFamily >= CHIP_FAMILY_R300)
154518781e08Smrg            driverNames[1] = driverNames[0];
154618781e08Smrg        else
154718781e08Smrg            driverNames[1] = NULL; /* no VDPAU support */
154818781e08Smrg
154918781e08Smrg	if (DRI2InfoCnt == 0) {
155018781e08Smrg	    if (!dixRegisterPrivateKey(dri2_window_private_key,
155118781e08Smrg				       PRIVATE_WINDOW,
155218781e08Smrg				       sizeof(struct dri2_window_priv))) {
155318781e08Smrg		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
155418781e08Smrg			   "Failed to get DRI2 window private\n");
1555de2362d3Smrg		return FALSE;
1556de2362d3Smrg	    }
1557de2362d3Smrg
1558de2362d3Smrg	    AddCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
1559de2362d3Smrg	}
1560de2362d3Smrg
156118781e08Smrg	DRI2InfoCnt++;
1562de2362d3Smrg    }
156318781e08Smrg
156418781e08Smrg#if DRI2INFOREC_VERSION >= 9
156518781e08Smrg    dri2_info.version = 9;
156618781e08Smrg    dri2_info.CreateBuffer2 = radeon_dri2_create_buffer2;
156718781e08Smrg    dri2_info.DestroyBuffer2 = radeon_dri2_destroy_buffer2;
156818781e08Smrg    dri2_info.CopyRegion2 = radeon_dri2_copy_region2;
1569de2362d3Smrg#endif
1570de2362d3Smrg
1571de2362d3Smrg    info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
1572de2362d3Smrg    return info->dri2.enabled;
1573de2362d3Smrg}
1574de2362d3Smrg
1575de2362d3Smrgvoid radeon_dri2_close_screen(ScreenPtr pScreen)
1576de2362d3Smrg{
1577de2362d3Smrg    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1578de2362d3Smrg    RADEONInfoPtr info = RADEONPTR(pScrn);
1579de2362d3Smrg
158018781e08Smrg    if (--DRI2InfoCnt == 0)
1581de2362d3Smrg    	DeleteCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
158218781e08Smrg
1583de2362d3Smrg    DRI2CloseScreen(pScreen);
1584de2362d3Smrg    drmFree(info->dri2.device_name);
1585de2362d3Smrg}
1586de2362d3Smrg
158718781e08Smrg#endif /* DRI2 */
158818781e08Smrg
1589