1/*
2 * Copyright © 2007, 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Soft-
6 * ware"), to deal in the Software without restriction, including without
7 * limitation the rights to use, copy, modify, merge, publish, distribute,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, provided that the above copyright
10 * notice(s) and this permission notice appear in all copies of the Soft-
11 * ware and that both the above copyright notice(s) and this permission
12 * notice appear in supporting documentation.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
17 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
18 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
19 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
22 * MANCE OF THIS SOFTWARE.
23 *
24 * Except as contained in this notice, the name of a copyright holder shall
25 * not be used in advertising or otherwise to promote the sale, use or
26 * other dealings in this Software without prior written authorization of
27 * the copyright holder.
28 *
29 * Authors:
30 *   Kristian Høgsberg (krh@redhat.com)
31 */
32
33#ifdef HAVE_XORG_CONFIG_H
34#include <xorg-config.h>
35#endif
36
37#include <errno.h>
38#ifdef WITH_LIBDRM
39#include <xf86drm.h>
40#endif
41#include "xf86Module.h"
42#include "list.h"
43#include "scrnintstr.h"
44#include "windowstr.h"
45#include "dixstruct.h"
46#include "dri2.h"
47#include "xf86VGAarbiter.h"
48
49#include "xf86.h"
50
51CARD8 dri2_major; /* version of DRI2 supported by DDX */
52CARD8 dri2_minor;
53
54static DevPrivateKeyRec dri2ScreenPrivateKeyRec;
55#define dri2ScreenPrivateKey (&dri2ScreenPrivateKeyRec)
56
57static DevPrivateKeyRec dri2WindowPrivateKeyRec;
58#define dri2WindowPrivateKey (&dri2WindowPrivateKeyRec)
59
60static DevPrivateKeyRec dri2PixmapPrivateKeyRec;
61#define dri2PixmapPrivateKey (&dri2PixmapPrivateKeyRec)
62
63static RESTYPE       dri2DrawableRes;
64
65typedef struct _DRI2Screen *DRI2ScreenPtr;
66
67typedef struct _DRI2Drawable {
68    DRI2ScreenPtr        dri2_screen;
69    DrawablePtr		 drawable;
70    struct list		 reference_list;
71    int			 width;
72    int			 height;
73    DRI2BufferPtr	*buffers;
74    int			 bufferCount;
75    unsigned int	 swapsPending;
76    ClientPtr		 blockedClient;
77    Bool		 blockedOnMsc;
78    int			 swap_interval;
79    CARD64		 swap_count;
80    int64_t		 target_sbc; /* -1 means no SBC wait outstanding */
81    CARD64		 last_swap_target; /* most recently queued swap target */
82    CARD64		 last_swap_msc; /* msc at completion of most recent swap */
83    CARD64		 last_swap_ust; /* ust at completion of most recent swap */
84    int			 swap_limit; /* for N-buffering */
85    unsigned long	 serialNumber;
86} DRI2DrawableRec, *DRI2DrawablePtr;
87
88typedef struct _DRI2Screen {
89    ScreenPtr			 screen;
90    int				 refcnt;
91    unsigned int		 numDrivers;
92    const char			**driverNames;
93    const char			*deviceName;
94    int				 fd;
95    unsigned int		 lastSequence;
96
97    DRI2CreateBufferProcPtr	 CreateBuffer;
98    DRI2DestroyBufferProcPtr	 DestroyBuffer;
99    DRI2CopyRegionProcPtr	 CopyRegion;
100    DRI2ScheduleSwapProcPtr	 ScheduleSwap;
101    DRI2GetMSCProcPtr		 GetMSC;
102    DRI2ScheduleWaitMSCProcPtr	 ScheduleWaitMSC;
103    DRI2AuthMagicProcPtr	 AuthMagic;
104
105    HandleExposuresProcPtr       HandleExposures;
106
107    ConfigNotifyProcPtr		 ConfigNotify;
108} DRI2ScreenRec;
109
110static DRI2ScreenPtr
111DRI2GetScreen(ScreenPtr pScreen)
112{
113    return dixLookupPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey);
114}
115
116static DRI2DrawablePtr
117DRI2GetDrawable(DrawablePtr pDraw)
118{
119    WindowPtr pWin;
120    PixmapPtr pPixmap;
121
122    switch (pDraw->type) {
123    case DRAWABLE_WINDOW:
124	pWin = (WindowPtr) pDraw;
125	return dixLookupPrivate(&pWin->devPrivates, dri2WindowPrivateKey);
126    case DRAWABLE_PIXMAP:
127	pPixmap = (PixmapPtr) pDraw;
128	return dixLookupPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey);
129    default:
130	return NULL;
131    }
132}
133
134static unsigned long
135DRI2DrawableSerial(DrawablePtr pDraw)
136{
137    ScreenPtr pScreen = pDraw->pScreen;
138    PixmapPtr pPix;
139
140    if (pDraw->type != DRAWABLE_WINDOW)
141	return pDraw->serialNumber;
142
143    pPix = pScreen->GetWindowPixmap((WindowPtr)pDraw);
144    return pPix->drawable.serialNumber;
145}
146
147static DRI2DrawablePtr
148DRI2AllocateDrawable(DrawablePtr pDraw)
149{
150    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
151    DRI2DrawablePtr pPriv;
152    CARD64          ust;
153    WindowPtr pWin;
154    PixmapPtr pPixmap;
155
156    pPriv = malloc(sizeof *pPriv);
157    if (pPriv == NULL)
158	return NULL;
159
160    pPriv->dri2_screen = ds;
161    pPriv->drawable = pDraw;
162    pPriv->width = pDraw->width;
163    pPriv->height = pDraw->height;
164    pPriv->buffers = NULL;
165    pPriv->bufferCount = 0;
166    pPriv->swapsPending = 0;
167    pPriv->blockedClient = NULL;
168    pPriv->blockedOnMsc = FALSE;
169    pPriv->swap_count = 0;
170    pPriv->target_sbc = -1;
171    pPriv->swap_interval = 1;
172    /* Initialize last swap target from DDX if possible */
173    if (!ds->GetMSC || !(*ds->GetMSC)(pDraw, &ust, &pPriv->last_swap_target))
174	pPriv->last_swap_target = 0;
175
176    pPriv->swap_limit = 1; /* default to double buffering */
177    pPriv->last_swap_msc = 0;
178    pPriv->last_swap_ust = 0;
179    list_init(&pPriv->reference_list);
180    pPriv->serialNumber = DRI2DrawableSerial(pDraw);
181
182    if (pDraw->type == DRAWABLE_WINDOW) {
183	pWin = (WindowPtr) pDraw;
184	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, pPriv);
185    } else {
186	pPixmap = (PixmapPtr) pDraw;
187	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, pPriv);
188    }
189
190    return pPriv;
191}
192
193typedef struct DRI2DrawableRefRec {
194    XID		  id;
195    XID		  dri2_id;
196    DRI2InvalidateProcPtr	invalidate;
197    void	 *priv;
198    struct list	  link;
199} DRI2DrawableRefRec, *DRI2DrawableRefPtr;
200
201static DRI2DrawableRefPtr
202DRI2LookupDrawableRef(DRI2DrawablePtr pPriv, XID id)
203{
204    DRI2DrawableRefPtr ref = NULL;
205
206    list_for_each_entry(ref, &pPriv->reference_list, link) {
207	if (ref->id == id)
208	    return ref;
209    }
210
211    return NULL;
212}
213
214static int
215DRI2AddDrawableRef(DRI2DrawablePtr pPriv, XID id, XID dri2_id,
216		   DRI2InvalidateProcPtr invalidate, void *priv)
217{
218    DRI2DrawableRefPtr ref;
219
220    ref = malloc(sizeof *ref);
221    if (ref == NULL)
222	return BadAlloc;
223
224    if (!AddResource(dri2_id, dri2DrawableRes, pPriv)) {
225	free(ref);
226	return BadAlloc;
227    }
228    if (!DRI2LookupDrawableRef(pPriv, id))
229	if (!AddResource(id, dri2DrawableRes, pPriv)) {
230	    FreeResourceByType(dri2_id, dri2DrawableRes, TRUE);
231	    free(ref);
232	    return BadAlloc;
233        }
234
235    ref->id = id;
236    ref->dri2_id = dri2_id;
237    ref->invalidate = invalidate;
238    ref->priv = priv;
239    list_add(&ref->link, &pPriv->reference_list);
240
241    return Success;
242}
243
244int
245DRI2CreateDrawable(ClientPtr client, DrawablePtr pDraw, XID id,
246		   DRI2InvalidateProcPtr invalidate, void *priv)
247{
248    DRI2DrawablePtr pPriv;
249    XID dri2_id;
250    int rc;
251
252    pPriv = DRI2GetDrawable(pDraw);
253    if (pPriv == NULL)
254	pPriv = DRI2AllocateDrawable(pDraw);
255    if (pPriv == NULL)
256	return BadAlloc;
257
258    dri2_id = FakeClientID(client->index);
259    rc = DRI2AddDrawableRef(pPriv, id, dri2_id, invalidate, priv);
260    if (rc != Success)
261	return rc;
262
263    return Success;
264}
265
266static int DRI2DrawableGone(pointer p, XID id)
267{
268    DRI2DrawablePtr pPriv = p;
269    DRI2ScreenPtr   ds = pPriv->dri2_screen;
270    DRI2DrawableRefPtr ref = NULL, next;
271    WindowPtr pWin;
272    PixmapPtr pPixmap;
273    DrawablePtr pDraw;
274    int i;
275
276    list_for_each_entry_safe(ref, next, &pPriv->reference_list, link) {
277	if (ref->dri2_id == id) {
278	    list_del(&ref->link);
279	    /* If this was the last ref under this X drawable XID,
280	     * unregister the X drawable resource. */
281	    if (!DRI2LookupDrawableRef(pPriv, ref->id))
282		FreeResourceByType(ref->id, dri2DrawableRes, TRUE);
283	    free(ref);
284	    break;
285	}
286
287	if (ref->id == id) {
288	    list_del(&ref->link);
289	    FreeResourceByType(ref->dri2_id, dri2DrawableRes, TRUE);
290	    free(ref);
291	}
292    }
293
294    if (!list_is_empty(&pPriv->reference_list))
295	return Success;
296
297    pDraw = pPriv->drawable;
298    if (pDraw->type == DRAWABLE_WINDOW) {
299	pWin = (WindowPtr) pDraw;
300	dixSetPrivate(&pWin->devPrivates, dri2WindowPrivateKey, NULL);
301    } else {
302	pPixmap = (PixmapPtr) pDraw;
303	dixSetPrivate(&pPixmap->devPrivates, dri2PixmapPrivateKey, NULL);
304    }
305
306    if (pPriv->buffers != NULL) {
307	for (i = 0; i < pPriv->bufferCount; i++)
308	    (*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
309
310	free(pPriv->buffers);
311    }
312
313    free(pPriv);
314
315    return Success;
316}
317
318static int
319find_attachment(DRI2DrawablePtr pPriv, unsigned attachment)
320{
321    int i;
322
323    if (pPriv->buffers == NULL) {
324	return -1;
325    }
326
327    for (i = 0; i < pPriv->bufferCount; i++) {
328	if ((pPriv->buffers[i] != NULL)
329	    && (pPriv->buffers[i]->attachment == attachment)) {
330	    return i;
331	}
332    }
333
334    return -1;
335}
336
337static Bool
338allocate_or_reuse_buffer(DrawablePtr pDraw, DRI2ScreenPtr ds,
339			 DRI2DrawablePtr pPriv,
340			 unsigned int attachment, unsigned int format,
341			 int dimensions_match, DRI2BufferPtr *buffer)
342{
343    int old_buf = find_attachment(pPriv, attachment);
344
345    if ((old_buf < 0)
346	|| !dimensions_match
347	|| (pPriv->buffers[old_buf]->format != format)) {
348	*buffer = (*ds->CreateBuffer)(pDraw, attachment, format);
349	pPriv->serialNumber = DRI2DrawableSerial(pDraw);
350	return TRUE;
351
352    } else {
353	*buffer = pPriv->buffers[old_buf];
354	pPriv->buffers[old_buf] = NULL;
355	return FALSE;
356    }
357}
358
359static void
360update_dri2_drawable_buffers(DRI2DrawablePtr pPriv, DrawablePtr pDraw,
361			     DRI2BufferPtr *buffers, int *out_count, int *width, int *height)
362{
363    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
364    int i;
365
366    if (pPriv->buffers != NULL) {
367	for (i = 0; i < pPriv->bufferCount; i++) {
368	    if (pPriv->buffers[i] != NULL) {
369		(*ds->DestroyBuffer)(pDraw, pPriv->buffers[i]);
370	    }
371	}
372
373	free(pPriv->buffers);
374    }
375
376    pPriv->buffers = buffers;
377    pPriv->bufferCount = *out_count;
378    pPriv->width = pDraw->width;
379    pPriv->height = pDraw->height;
380    *width = pPriv->width;
381    *height = pPriv->height;
382}
383
384static DRI2BufferPtr *
385do_get_buffers(DrawablePtr pDraw, int *width, int *height,
386	       unsigned int *attachments, int count, int *out_count,
387	       int has_format)
388{
389    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
390    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
391    DRI2BufferPtr  *buffers;
392    int need_real_front = 0;
393    int need_fake_front = 0;
394    int have_fake_front = 0;
395    int front_format = 0;
396    int dimensions_match;
397    int buffers_changed = 0;
398    int i;
399
400    if (!pPriv) {
401	*width = pDraw->width;
402	*height = pDraw->height;
403	*out_count = 0;
404	return NULL;
405    }
406
407    dimensions_match = (pDraw->width == pPriv->width)
408	&& (pDraw->height == pPriv->height)
409	&& (pPriv->serialNumber == DRI2DrawableSerial(pDraw));
410
411    buffers = calloc((count + 1), sizeof(buffers[0]));
412
413    for (i = 0; i < count; i++) {
414	const unsigned attachment = *(attachments++);
415	const unsigned format = (has_format) ? *(attachments++) : 0;
416
417	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, attachment,
418				     format, dimensions_match,
419				     &buffers[i]))
420		buffers_changed = 1;
421
422	if (buffers[i] == NULL)
423	    goto err_out;
424
425	/* If the drawable is a window and the front-buffer is requested,
426	 * silently add the fake front-buffer to the list of requested
427	 * attachments.  The counting logic in the loop accounts for the case
428	 * where the client requests both the fake and real front-buffer.
429	 */
430	if (attachment == DRI2BufferBackLeft) {
431	    need_real_front++;
432	    front_format = format;
433	}
434
435	if (attachment == DRI2BufferFrontLeft) {
436	    need_real_front--;
437	    front_format = format;
438
439	    if (pDraw->type == DRAWABLE_WINDOW) {
440		need_fake_front++;
441	    }
442	}
443
444	if (pDraw->type == DRAWABLE_WINDOW) {
445	    if (attachment == DRI2BufferFakeFrontLeft) {
446		need_fake_front--;
447		have_fake_front = 1;
448	    }
449	}
450    }
451
452    if (need_real_front > 0) {
453	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFrontLeft,
454				     front_format, dimensions_match,
455				     &buffers[i]))
456	    buffers_changed = 1;
457
458	if (buffers[i] == NULL)
459	    goto err_out;
460	i++;
461    }
462
463    if (need_fake_front > 0) {
464	if (allocate_or_reuse_buffer(pDraw, ds, pPriv, DRI2BufferFakeFrontLeft,
465				     front_format, dimensions_match,
466				     &buffers[i]))
467	    buffers_changed = 1;
468
469	if (buffers[i] == NULL)
470	    goto err_out;
471
472	i++;
473	have_fake_front = 1;
474    }
475
476    *out_count = i;
477
478    update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, height);
479
480    /* If the client is getting a fake front-buffer, pre-fill it with the
481     * contents of the real front-buffer.  This ensures correct operation of
482     * applications that call glXWaitX before calling glDrawBuffer.
483     */
484    if (have_fake_front && buffers_changed) {
485	BoxRec box;
486	RegionRec region;
487
488	box.x1 = 0;
489	box.y1 = 0;
490	box.x2 = pPriv->width;
491	box.y2 = pPriv->height;
492	RegionInit(&region, &box, 0);
493
494	DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
495		       DRI2BufferFrontLeft);
496    }
497
498    return pPriv->buffers;
499
500err_out:
501
502    *out_count = 0;
503
504    for (i = 0; i < count; i++) {
505	    if (buffers[i] != NULL)
506		    (*ds->DestroyBuffer)(pDraw, buffers[i]);
507    }
508
509    free(buffers);
510    buffers = NULL;
511
512    update_dri2_drawable_buffers(pPriv, pDraw, buffers, out_count, width, height);
513
514    return buffers;
515}
516
517DRI2BufferPtr *
518DRI2GetBuffers(DrawablePtr pDraw, int *width, int *height,
519	       unsigned int *attachments, int count, int *out_count)
520{
521    return do_get_buffers(pDraw, width, height, attachments, count,
522			  out_count, FALSE);
523}
524
525DRI2BufferPtr *
526DRI2GetBuffersWithFormat(DrawablePtr pDraw, int *width, int *height,
527			 unsigned int *attachments, int count, int *out_count)
528{
529    return do_get_buffers(pDraw, width, height, attachments, count,
530			  out_count, TRUE);
531}
532
533static void
534DRI2InvalidateDrawable(DrawablePtr pDraw)
535{
536    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDraw);
537    DRI2DrawableRefPtr ref = NULL;
538
539    if (!pPriv)
540        return;
541
542    list_for_each_entry(ref, &pPriv->reference_list, link)
543	ref->invalidate(pDraw, ref->priv);
544}
545
546/*
547 * In the direct rendered case, we throttle the clients that have more
548 * than their share of outstanding swaps (and thus busy buffers) when a
549 * new GetBuffers request is received.  In the AIGLX case, we allow the
550 * client to get the new buffers, but throttle when the next GLX request
551 * comes in (see __glXDRIcontextWait()).
552 */
553Bool
554DRI2ThrottleClient(ClientPtr client, DrawablePtr pDraw)
555{
556    DRI2DrawablePtr pPriv;
557
558    pPriv = DRI2GetDrawable(pDraw);
559    if (pPriv == NULL)
560	return FALSE;
561
562    /* Throttle to swap limit */
563    if ((pPriv->swapsPending >= pPriv->swap_limit) &&
564	!pPriv->blockedClient) {
565	ResetCurrentRequest(client);
566	client->sequence--;
567	IgnoreClient(client);
568	pPriv->blockedClient = client;
569	return TRUE;
570    }
571
572    return FALSE;
573}
574
575static void
576__DRI2BlockClient(ClientPtr client, DRI2DrawablePtr pPriv)
577{
578    if (pPriv->blockedClient == NULL) {
579	IgnoreClient(client);
580	pPriv->blockedClient = client;
581    }
582}
583
584void
585DRI2BlockClient(ClientPtr client, DrawablePtr pDraw)
586{
587    DRI2DrawablePtr pPriv;
588
589    pPriv = DRI2GetDrawable(pDraw);
590    if (pPriv == NULL)
591	return;
592
593    __DRI2BlockClient(client, pPriv);
594    pPriv->blockedOnMsc = TRUE;
595}
596
597int
598DRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion,
599	       unsigned int dest, unsigned int src)
600{
601    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
602    DRI2DrawablePtr pPriv;
603    DRI2BufferPtr   pDestBuffer, pSrcBuffer;
604    int		    i;
605
606    pPriv = DRI2GetDrawable(pDraw);
607    if (pPriv == NULL)
608	return BadDrawable;
609
610    pDestBuffer = NULL;
611    pSrcBuffer = NULL;
612    for (i = 0; i < pPriv->bufferCount; i++)
613    {
614	if (pPriv->buffers[i]->attachment == dest)
615	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
616	if (pPriv->buffers[i]->attachment == src)
617	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
618    }
619    if (pSrcBuffer == NULL || pDestBuffer == NULL)
620	return BadValue;
621
622    (*ds->CopyRegion)(pDraw, pRegion, pDestBuffer, pSrcBuffer);
623
624    return Success;
625}
626
627/* Can this drawable be page flipped? */
628Bool
629DRI2CanFlip(DrawablePtr pDraw)
630{
631    ScreenPtr pScreen = pDraw->pScreen;
632    WindowPtr pWin, pRoot;
633    PixmapPtr pWinPixmap, pRootPixmap;
634
635    if (pDraw->type == DRAWABLE_PIXMAP)
636	return TRUE;
637
638    pRoot = pScreen->root;
639    pRootPixmap = pScreen->GetWindowPixmap(pRoot);
640
641    pWin = (WindowPtr) pDraw;
642    pWinPixmap = pScreen->GetWindowPixmap(pWin);
643    if (pRootPixmap != pWinPixmap)
644	return FALSE;
645    if (!RegionEqual(&pWin->clipList, &pRoot->winSize))
646	return FALSE;
647
648    /* Does the window match the pixmap exactly? */
649    if (pDraw->x != 0 ||
650	pDraw->y != 0 ||
651#ifdef COMPOSITE
652	pDraw->x != pWinPixmap->screen_x ||
653	pDraw->y != pWinPixmap->screen_y ||
654#endif
655	pDraw->width != pWinPixmap->drawable.width ||
656	pDraw->height != pWinPixmap->drawable.height)
657	return FALSE;
658
659    return TRUE;
660}
661
662/* Can we do a pixmap exchange instead of a blit? */
663Bool
664DRI2CanExchange(DrawablePtr pDraw)
665{
666    return FALSE;
667}
668
669void
670DRI2WaitMSCComplete(ClientPtr client, DrawablePtr pDraw, int frame,
671		    unsigned int tv_sec, unsigned int tv_usec)
672{
673    DRI2DrawablePtr pPriv;
674
675    pPriv = DRI2GetDrawable(pDraw);
676    if (pPriv == NULL)
677	return;
678
679    ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
680			 frame, pPriv->swap_count);
681
682    if (pPriv->blockedClient)
683	AttendClient(pPriv->blockedClient);
684
685    pPriv->blockedClient = NULL;
686    pPriv->blockedOnMsc = FALSE;
687}
688
689static void
690DRI2WakeClient(ClientPtr client, DrawablePtr pDraw, int frame,
691	       unsigned int tv_sec, unsigned int tv_usec)
692{
693    ScreenPtr	    pScreen = pDraw->pScreen;
694    DRI2DrawablePtr pPriv;
695
696    pPriv = DRI2GetDrawable(pDraw);
697    if (pPriv == NULL) {
698        xf86DrvMsg(pScreen->myNum, X_ERROR,
699		   "[DRI2] %s: bad drawable\n", __func__);
700	return;
701    }
702
703    /*
704     * Swap completed.
705     * Wake the client iff:
706     *   - it was waiting on SBC
707     *   - was blocked due to GLX make current
708     *   - was blocked due to swap throttling
709     *   - is not blocked due to an MSC wait
710     */
711    if (pPriv->target_sbc != -1 &&
712	pPriv->target_sbc <= pPriv->swap_count) {
713	ProcDRI2WaitMSCReply(client, ((CARD64)tv_sec * 1000000) + tv_usec,
714			     frame, pPriv->swap_count);
715	pPriv->target_sbc = -1;
716
717	AttendClient(pPriv->blockedClient);
718	pPriv->blockedClient = NULL;
719    } else if (pPriv->target_sbc == -1 && !pPriv->blockedOnMsc) {
720	if (pPriv->blockedClient) {
721	    AttendClient(pPriv->blockedClient);
722	    pPriv->blockedClient = NULL;
723	}
724    }
725}
726
727void
728DRI2SwapComplete(ClientPtr client, DrawablePtr pDraw, int frame,
729		   unsigned int tv_sec, unsigned int tv_usec, int type,
730		   DRI2SwapEventPtr swap_complete, void *swap_data)
731{
732    ScreenPtr	    pScreen = pDraw->pScreen;
733    DRI2DrawablePtr pPriv;
734    CARD64          ust = 0;
735    BoxRec          box;
736    RegionRec       region;
737
738    pPriv = DRI2GetDrawable(pDraw);
739    if (pPriv == NULL) {
740        xf86DrvMsg(pScreen->myNum, X_ERROR,
741		   "[DRI2] %s: bad drawable\n", __func__);
742	return;
743    }
744
745    pPriv->swapsPending--;
746    pPriv->swap_count++;
747
748    box.x1 = 0;
749    box.y1 = 0;
750    box.x2 = pDraw->width;
751    box.y2 = pDraw->height;
752    RegionInit(&region, &box, 0);
753    DRI2CopyRegion(pDraw, &region, DRI2BufferFakeFrontLeft,
754		   DRI2BufferFrontLeft);
755
756    ust = ((CARD64)tv_sec * 1000000) + tv_usec;
757    if (swap_complete)
758	swap_complete(client, swap_data, type, ust, frame, pPriv->swap_count);
759
760    pPriv->last_swap_msc = frame;
761    pPriv->last_swap_ust = ust;
762
763    DRI2WakeClient(client, pDraw, frame, tv_sec, tv_usec);
764}
765
766Bool
767DRI2WaitSwap(ClientPtr client, DrawablePtr pDrawable)
768{
769    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
770
771    /* If we're currently waiting for a swap on this drawable, reset
772     * the request and suspend the client.  We only support one
773     * blocked client per drawable. */
774    if (pPriv &&
775	pPriv->swapsPending &&
776	pPriv->blockedClient == NULL) {
777	ResetCurrentRequest(client);
778	client->sequence--;
779	__DRI2BlockClient(client, pPriv);
780	return TRUE;
781    }
782
783    return FALSE;
784}
785
786int
787DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
788		CARD64 divisor, CARD64 remainder, CARD64 *swap_target,
789		DRI2SwapEventPtr func, void *data)
790{
791    ScreenPtr       pScreen = pDraw->pScreen;
792    DRI2ScreenPtr   ds = DRI2GetScreen(pDraw->pScreen);
793    DRI2DrawablePtr pPriv;
794    DRI2BufferPtr   pDestBuffer = NULL, pSrcBuffer = NULL;
795    int             ret, i;
796    CARD64          ust, current_msc;
797
798    pPriv = DRI2GetDrawable(pDraw);
799    if (pPriv == NULL) {
800        xf86DrvMsg(pScreen->myNum, X_ERROR,
801		   "[DRI2] %s: bad drawable\n", __func__);
802	return BadDrawable;
803    }
804
805    for (i = 0; i < pPriv->bufferCount; i++) {
806	if (pPriv->buffers[i]->attachment == DRI2BufferFrontLeft)
807	    pDestBuffer = (DRI2BufferPtr) pPriv->buffers[i];
808	if (pPriv->buffers[i]->attachment == DRI2BufferBackLeft)
809	    pSrcBuffer = (DRI2BufferPtr) pPriv->buffers[i];
810    }
811    if (pSrcBuffer == NULL || pDestBuffer == NULL) {
812        xf86DrvMsg(pScreen->myNum, X_ERROR,
813		   "[DRI2] %s: drawable has no back or front?\n", __func__);
814	return BadDrawable;
815    }
816
817    /* Old DDX or no swap interval, just blit */
818    if (!ds->ScheduleSwap || !pPriv->swap_interval) {
819	BoxRec box;
820	RegionRec region;
821
822	box.x1 = 0;
823	box.y1 = 0;
824	box.x2 = pDraw->width;
825	box.y2 = pDraw->height;
826	RegionInit(&region, &box, 0);
827
828	pPriv->swapsPending++;
829
830	(*ds->CopyRegion)(pDraw, &region, pDestBuffer, pSrcBuffer);
831	DRI2SwapComplete(client, pDraw, target_msc, 0, 0, DRI2_BLIT_COMPLETE,
832			 func, data);
833	return Success;
834    }
835
836    /*
837     * In the simple glXSwapBuffers case, all params will be 0, and we just
838     * need to schedule a swap for the last swap target + the swap interval.
839     */
840    if (target_msc == 0 && divisor == 0 && remainder == 0) {
841	/* If the current vblank count of the drawable's crtc is lower
842	 * than the count stored in last_swap_target from a previous swap
843	 * then reinitialize last_swap_target to the current crtc's msc,
844	 * otherwise the swap will hang. This will happen if the drawable
845	 * is moved to a crtc with a lower refresh rate, or a crtc that just
846	 * got enabled.
847	 */
848	if (ds->GetMSC) {
849	    if (!(*ds->GetMSC)(pDraw, &ust, &current_msc))
850		pPriv->last_swap_target = 0;
851
852	    if (current_msc < pPriv->last_swap_target)
853		pPriv->last_swap_target = current_msc;
854
855	}
856
857	/*
858	 * Swap target for this swap is last swap target + swap interval since
859	 * we have to account for the current swap count, interval, and the
860	 * number of pending swaps.
861	 */
862	*swap_target = pPriv->last_swap_target + pPriv->swap_interval;
863
864    } else {
865	/* glXSwapBuffersMscOML could have a 0 target_msc, honor it */
866	*swap_target = target_msc;
867    }
868
869    pPriv->swapsPending++;
870    ret = (*ds->ScheduleSwap)(client, pDraw, pDestBuffer, pSrcBuffer,
871			      swap_target, divisor, remainder, func, data);
872    if (!ret) {
873	pPriv->swapsPending--; /* didn't schedule */
874        xf86DrvMsg(pScreen->myNum, X_ERROR,
875		   "[DRI2] %s: driver failed to schedule swap\n", __func__);
876	return BadDrawable;
877    }
878
879    pPriv->last_swap_target = *swap_target;
880
881    /* According to spec, return expected swapbuffers count SBC after this swap
882     * will complete.
883     */
884    *swap_target = pPriv->swap_count + pPriv->swapsPending;
885
886    DRI2InvalidateDrawable(pDraw);
887
888    return Success;
889}
890
891void
892DRI2SwapInterval(DrawablePtr pDrawable, int interval)
893{
894    ScreenPtr       pScreen = pDrawable->pScreen;
895    DRI2DrawablePtr pPriv = DRI2GetDrawable(pDrawable);
896
897    if (pPriv == NULL) {
898        xf86DrvMsg(pScreen->myNum, X_ERROR,
899		   "[DRI2] %s: bad drawable\n", __func__);
900	return;
901    }
902
903    /* fixme: check against arbitrary max? */
904    pPriv->swap_interval = interval;
905}
906
907int
908DRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc, CARD64 *sbc)
909{
910    ScreenPtr pScreen = pDraw->pScreen;
911    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
912    DRI2DrawablePtr pPriv;
913    Bool ret;
914
915    pPriv = DRI2GetDrawable(pDraw);
916    if (pPriv == NULL) {
917        xf86DrvMsg(pScreen->myNum, X_ERROR,
918		   "[DRI2] %s: bad drawable\n", __func__);
919	return BadDrawable;
920    }
921
922    if (!ds->GetMSC) {
923	*ust = 0;
924	*msc = 0;
925	*sbc = pPriv->swap_count;
926	return Success;
927    }
928
929    /*
930     * Spec needs to be updated to include unmapped or redirected
931     * drawables
932     */
933
934    ret = (*ds->GetMSC)(pDraw, ust, msc);
935    if (!ret)
936	return BadDrawable;
937
938    *sbc = pPriv->swap_count;
939
940    return Success;
941}
942
943int
944DRI2WaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
945	    CARD64 divisor, CARD64 remainder)
946{
947    DRI2ScreenPtr ds = DRI2GetScreen(pDraw->pScreen);
948    DRI2DrawablePtr pPriv;
949    Bool ret;
950
951    pPriv = DRI2GetDrawable(pDraw);
952    if (pPriv == NULL)
953	return BadDrawable;
954
955    /* Old DDX just completes immediately */
956    if (!ds->ScheduleWaitMSC) {
957	DRI2WaitMSCComplete(client, pDraw, target_msc, 0, 0);
958
959	return Success;
960    }
961
962    ret = (*ds->ScheduleWaitMSC)(client, pDraw, target_msc, divisor, remainder);
963    if (!ret)
964	return BadDrawable;
965
966    return Success;
967}
968
969int
970DRI2WaitSBC(ClientPtr client, DrawablePtr pDraw, CARD64 target_sbc)
971{
972    DRI2DrawablePtr pPriv;
973
974    pPriv = DRI2GetDrawable(pDraw);
975    if (pPriv == NULL)
976	return BadDrawable;
977
978    /* target_sbc == 0 means to block until all pending swaps are
979     * finished. Recalculate target_sbc to get that behaviour.
980     */
981    if (target_sbc == 0)
982        target_sbc = pPriv->swap_count + pPriv->swapsPending;
983
984    /* If current swap count already >= target_sbc, reply and
985     * return immediately with (ust, msc, sbc) triplet of
986     * most recent completed swap.
987     */
988    if (pPriv->swap_count >= target_sbc) {
989        ProcDRI2WaitMSCReply(client, pPriv->last_swap_ust,
990                             pPriv->last_swap_msc, pPriv->swap_count);
991        return Success;
992    }
993
994    pPriv->target_sbc = target_sbc;
995    __DRI2BlockClient(client, pPriv);
996
997    return Success;
998}
999
1000Bool
1001DRI2HasSwapControl(ScreenPtr pScreen)
1002{
1003    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1004
1005    return ds->ScheduleSwap && ds->GetMSC;
1006}
1007
1008Bool
1009DRI2Connect(ScreenPtr pScreen, unsigned int driverType, int *fd,
1010	    const char **driverName, const char **deviceName)
1011{
1012    DRI2ScreenPtr ds;
1013
1014    if (!dixPrivateKeyRegistered(dri2ScreenPrivateKey))
1015	return FALSE;
1016
1017    ds = DRI2GetScreen(pScreen);
1018    if (ds == NULL || driverType >= ds->numDrivers ||
1019	    !ds->driverNames[driverType])
1020	return FALSE;
1021
1022    *fd = ds->fd;
1023    *driverName = ds->driverNames[driverType];
1024    *deviceName = ds->deviceName;
1025
1026    return TRUE;
1027}
1028
1029Bool
1030DRI2Authenticate(ScreenPtr pScreen, uint32_t magic)
1031{
1032    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1033
1034    if (ds == NULL || (*ds->AuthMagic)(ds->fd, magic))
1035        return FALSE;
1036
1037    return TRUE;
1038}
1039
1040static int
1041DRI2ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw,
1042		 WindowPtr pSib)
1043{
1044    DrawablePtr pDraw = (DrawablePtr)pWin;
1045    ScreenPtr pScreen = pDraw->pScreen;
1046    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1047    DRI2DrawablePtr dd = DRI2GetDrawable(pDraw);
1048    int ret;
1049
1050    if (ds->ConfigNotify) {
1051	pScreen->ConfigNotify = ds->ConfigNotify;
1052
1053	ret = (*pScreen->ConfigNotify)(pWin, x, y, w, h, bw, pSib);
1054
1055	ds->ConfigNotify = pScreen->ConfigNotify;
1056	pScreen->ConfigNotify = DRI2ConfigNotify;
1057	if (ret)
1058	    return ret;
1059    }
1060
1061    if (!dd || (dd->width == w && dd->height == h))
1062	return Success;
1063
1064    DRI2InvalidateDrawable(pDraw);
1065    return Success;
1066}
1067
1068Bool
1069DRI2ScreenInit(ScreenPtr pScreen, DRI2InfoPtr info)
1070{
1071    DRI2ScreenPtr ds;
1072    const char* driverTypeNames[] = {
1073	"DRI", /* DRI2DriverDRI */
1074	"VDPAU", /* DRI2DriverVDPAU */
1075    };
1076    unsigned int i;
1077    CARD8 cur_minor;
1078
1079    if (info->version < 3)
1080	return FALSE;
1081
1082    if (!xf86VGAarbiterAllowDRI(pScreen)) {
1083        xf86DrvMsg(pScreen->myNum, X_WARNING,
1084                  "[DRI2] Direct rendering is not supported when VGA arb is necessary for the device\n");
1085        return FALSE;
1086    }
1087
1088    if (!dixRegisterPrivateKey(&dri2ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1089	return FALSE;
1090
1091    if (!dixRegisterPrivateKey(&dri2WindowPrivateKeyRec, PRIVATE_WINDOW, 0))
1092	return FALSE;
1093
1094    if (!dixRegisterPrivateKey(&dri2PixmapPrivateKeyRec, PRIVATE_PIXMAP, 0))
1095	return FALSE;
1096
1097    ds = calloc(1, sizeof *ds);
1098    if (!ds)
1099	return FALSE;
1100
1101    ds->screen         = pScreen;
1102    ds->fd	       = info->fd;
1103    ds->deviceName     = info->deviceName;
1104    dri2_major         = 1;
1105
1106    ds->CreateBuffer   = info->CreateBuffer;
1107    ds->DestroyBuffer  = info->DestroyBuffer;
1108    ds->CopyRegion     = info->CopyRegion;
1109
1110    if (info->version >= 4) {
1111	ds->ScheduleSwap = info->ScheduleSwap;
1112	ds->ScheduleWaitMSC = info->ScheduleWaitMSC;
1113	ds->GetMSC = info->GetMSC;
1114	cur_minor = 3;
1115    } else {
1116	cur_minor = 1;
1117    }
1118
1119    if (info->version >= 5) {
1120        ds->AuthMagic = info->AuthMagic;
1121    }
1122
1123    /*
1124     * if the driver doesn't provide an AuthMagic function or the info struct
1125     * version is too low, it relies on the old method (using libdrm) or fail
1126     */
1127    if (!ds->AuthMagic)
1128#ifdef WITH_LIBDRM
1129        ds->AuthMagic = drmAuthMagic;
1130#else
1131        goto err_out;
1132#endif
1133
1134    /* Initialize minor if needed and set to minimum provied by DDX */
1135    if (!dri2_minor || dri2_minor > cur_minor)
1136	dri2_minor = cur_minor;
1137
1138    if (info->version == 3 || info->numDrivers == 0) {
1139	/* Driver too old: use the old-style driverName field */
1140	ds->numDrivers = 1;
1141	ds->driverNames = malloc(sizeof(*ds->driverNames));
1142	if (!ds->driverNames)
1143	    goto err_out;
1144	ds->driverNames[0] = info->driverName;
1145    } else {
1146	ds->numDrivers = info->numDrivers;
1147	ds->driverNames = malloc(info->numDrivers * sizeof(*ds->driverNames));
1148	if (!ds->driverNames)
1149		goto err_out;
1150	memcpy(ds->driverNames, info->driverNames,
1151	       info->numDrivers * sizeof(*ds->driverNames));
1152    }
1153
1154    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, ds);
1155
1156    ds->ConfigNotify = pScreen->ConfigNotify;
1157    pScreen->ConfigNotify = DRI2ConfigNotify;
1158
1159    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2] Setup complete\n");
1160    for (i = 0; i < sizeof(driverTypeNames) / sizeof(driverTypeNames[0]); i++) {
1161	if (i < ds->numDrivers && ds->driverNames[i]) {
1162	    xf86DrvMsg(pScreen->myNum, X_INFO, "[DRI2]   %s driver: %s\n",
1163		       driverTypeNames[i], ds->driverNames[i]);
1164	}
1165    }
1166
1167    return TRUE;
1168
1169err_out:
1170    xf86DrvMsg(pScreen->myNum, X_WARNING,
1171            "[DRI2] Initialization failed for info version %d.\n", info->version);
1172    free(ds);
1173    return FALSE;
1174}
1175
1176void
1177DRI2CloseScreen(ScreenPtr pScreen)
1178{
1179    DRI2ScreenPtr ds = DRI2GetScreen(pScreen);
1180
1181    free(ds->driverNames);
1182    free(ds);
1183    dixSetPrivate(&pScreen->devPrivates, dri2ScreenPrivateKey, NULL);
1184}
1185
1186extern ExtensionModule dri2ExtensionModule;
1187extern Bool DRI2ModuleSetup(void);
1188
1189/* Called by InitExtensions() */
1190Bool
1191DRI2ModuleSetup(void)
1192{
1193    dri2DrawableRes = CreateNewResourceType(DRI2DrawableGone, "DRI2Drawable");
1194    if (!dri2DrawableRes)
1195	return FALSE;
1196
1197    return TRUE;
1198}
1199
1200static pointer
1201DRI2Setup(pointer module, pointer opts, int *errmaj, int *errmin)
1202{
1203    static Bool setupDone = FALSE;
1204
1205    if (!setupDone)
1206    {
1207	setupDone = TRUE;
1208	LoadExtension(&dri2ExtensionModule, FALSE);
1209    }
1210    else
1211    {
1212	if (errmaj)
1213	    *errmaj = LDR_ONCEONLY;
1214    }
1215
1216    return (pointer) 1;
1217}
1218
1219static XF86ModuleVersionInfo DRI2VersRec =
1220{
1221    "dri2",
1222    MODULEVENDORSTRING,
1223    MODINFOSTRING1,
1224    MODINFOSTRING2,
1225    XORG_VERSION_CURRENT,
1226    1, 2, 0,
1227    ABI_CLASS_EXTENSION,
1228    ABI_EXTENSION_VERSION,
1229    MOD_CLASS_NONE,
1230    { 0, 0, 0, 0 }
1231};
1232
1233_X_EXPORT XF86ModuleData dri2ModuleData = { &DRI2VersRec, DRI2Setup, NULL };
1234
1235void
1236DRI2Version(int *major, int *minor)
1237{
1238    if (major != NULL)
1239	*major = DRI2VersRec.majorversion;
1240
1241    if (minor != NULL)
1242	*minor = DRI2VersRec.minorversion;
1243}
1244