1/*
2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
3 * Copyright 2011 VMWare, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 *
27 * Author: Alan Hourihane <alanh@tungstengraphics.com>
28 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
29 * Author: Thomas Hellstrom <thellstrom@vmware.com>
30 *
31 */
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
36#include "xorg-server.h"
37#include "xf86.h"
38#include "xf86_OSproc.h"
39
40#include "vmwgfx_driver.h"
41#include "../saa/saa.h"
42
43#include "dri2.h"
44#include "gcstruct.h"
45#include "gc.h"
46#include "vmwgfx_saa.h"
47#include "wsbm_util.h"
48#include <unistd.h>
49#include "vmwgfx_hosted.h"
50
51#define VMWGFX_FD_PATH_LEN 80
52
53typedef struct {
54    int refcount;
55    PixmapPtr pPixmap;
56    struct xa_surface *srf;
57    unsigned int dri2_depth;
58} *BufferPrivatePtr;
59
60
61/*
62 * Attempt to guess what the dri state tracker is up to.
63 * Currently it sends only bpp as format.
64 */
65
66static unsigned int
67vmwgfx_color_format_to_depth(unsigned int format)
68{
69    return format;
70}
71
72static unsigned int
73vmwgfx_zs_format_to_depth(unsigned int format)
74{
75    if (format == 24)
76	return 32;
77    return format;
78}
79
80static unsigned int
81vmwgfx_z_format_to_depth(unsigned int format)
82{
83    return format;
84}
85
86static Bool
87dri2_do_create_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer, unsigned int format)
88{
89    ScreenPtr pScreen = pDraw->pScreen;
90    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
91    modesettingPtr ms = modesettingPTR(pScrn);
92    BufferPrivatePtr private = buffer->driverPrivate;
93    PixmapPtr pPixmap;
94    struct vmwgfx_saa_pixmap *vpix;
95    struct xa_surface *srf = NULL;
96    unsigned int depth;
97
98
99    if (pDraw->type == DRAWABLE_PIXMAP)
100	pPixmap = (PixmapPtr) pDraw;
101    else
102	pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw);
103
104    vpix = vmwgfx_saa_pixmap(pPixmap);
105    private->refcount = 0;
106
107    switch (buffer->attachment) {
108    default:
109	depth = (format) ? vmwgfx_color_format_to_depth(format) :
110	    pDraw->depth;
111
112	if (buffer->attachment != DRI2BufferFakeFrontLeft ||
113	    &pPixmap->drawable != pDraw) {
114
115	    pPixmap = (*pScreen->CreatePixmap)(pScreen,
116					       pDraw->width,
117					       pDraw->height,
118					       depth,
119					       0);
120	    if (pPixmap == NullPixmap)
121		return FALSE;
122
123	    private->pPixmap = pPixmap;
124	    private->dri2_depth = depth;
125	    vpix = vmwgfx_saa_pixmap(pPixmap);
126	}
127	break;
128    case DRI2BufferFrontLeft:
129      if (&pPixmap->drawable == pDraw)
130	  break;
131      buffer->name = 0;
132      buffer->pitch = 0;
133      buffer->cpp = pDraw->bitsPerPixel / 8;
134      buffer->driverPrivate = private;
135      buffer->flags = 0; /* not tiled */
136      buffer->format = pDraw->bitsPerPixel;
137      if (!private->pPixmap) {
138	private->dri2_depth = 0;
139	private->pPixmap = pPixmap;
140	pPixmap->refcnt++;
141      }
142      return TRUE;
143    case DRI2BufferStencil:
144    case DRI2BufferDepthStencil:
145	if (!pScrn->vtSema)
146	    return FALSE;
147
148	depth = (format) ? vmwgfx_zs_format_to_depth(format) : 32;
149
150	/*
151	 * The SVGA device uses the zs ordering.
152	 */
153
154	srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
155				depth, xa_type_zs, xa_format_unknown,
156				XA_FLAG_SHARED );
157	if (!srf)
158	    return FALSE;
159
160	private->dri2_depth = depth;
161
162       break;
163    case DRI2BufferDepth:
164	if (!pScrn->vtSema)
165	    return FALSE;
166
167	depth = (format) ? vmwgfx_z_format_to_depth(format) :
168	    pDraw->bitsPerPixel;
169
170	if (depth == 24)
171	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
172				    depth, xa_type_zs, xa_format_unknown,
173				    XA_FLAG_SHARED );
174	else
175	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
176				    depth,
177				    xa_type_z, xa_format_unknown,
178				    XA_FLAG_SHARED);
179	if (!srf)
180	    return FALSE;
181
182	private->dri2_depth = depth;
183
184	break;
185    }
186
187    if (!private->pPixmap) {
188	private->pPixmap = pPixmap;
189	pPixmap->refcnt++;
190    }
191
192    if (!srf) {
193	depth = (format) ? vmwgfx_color_format_to_depth(format) :
194	    pDraw->depth;
195
196	if (!vmwgfx_hw_dri2_validate(pPixmap, depth))
197	    return FALSE;
198
199	srf = vpix->hw;
200	private->refcount++;
201	private->dri2_depth = depth;
202
203	/*
204	 * Compiz workaround. See vmwgfx_dirty();
205	 */
206
207	if (buffer->attachment == DRI2BufferFrontLeft ||
208	    buffer->attachment == DRI2BufferFakeFrontLeft)
209	    vpix->hw_is_dri2_fronts++;
210    }
211
212    private->srf = srf;
213    if (_xa_surface_handle(srf, &buffer->name, &buffer->pitch) != 0)
214	return FALSE;
215
216    buffer->cpp = xa_format_depth(xa_surface_format(srf)) / 8;
217    buffer->driverPrivate = private;
218    buffer->flags = 0; /* not tiled */
219    buffer->format = format;
220    private->refcount++;
221
222    return TRUE;
223}
224
225static void
226dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer)
227{
228    BufferPrivatePtr private = buffer->driverPrivate;
229    struct xa_surface *srf = private->srf;
230    ScreenPtr pScreen = pDraw->pScreen;
231    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(private->pPixmap);
232
233    if (--private->refcount == 0 && srf) {
234	xa_surface_destroy(srf);
235    }
236
237    /*
238     * Compiz workaround. See vmwgfx_dirty();
239     */
240
241    if ((buffer->attachment == DRI2BufferFrontLeft ||
242	 buffer->attachment == DRI2BufferFakeFrontLeft) &&
243	private->refcount == 1 &&
244	--vpix->hw_is_dri2_fronts == 0)
245	WSBMLISTDELINIT(&vpix->sync_x_head);
246
247    private->srf = NULL;
248    pScreen->DestroyPixmap(private->pPixmap);
249}
250
251
252static DRI2Buffer2Ptr
253dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format)
254{
255    DRI2Buffer2Ptr buffer;
256    BufferPrivatePtr private;
257
258    buffer = calloc(1, sizeof *buffer);
259    if (!buffer)
260	return NULL;
261
262    private = calloc(1, sizeof *private);
263    if (!private) {
264	goto fail;
265    }
266
267    buffer->attachment = attachment;
268    buffer->driverPrivate = private;
269
270    if (dri2_do_create_buffer(pDraw, buffer, format))
271	return buffer;
272
273    free(private);
274fail:
275    free(buffer);
276    return NULL;
277}
278
279static void
280dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer)
281{
282    /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */
283    dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer);
284
285    free(buffer->driverPrivate);
286    free(buffer);
287}
288
289static void
290dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
291                 DRI2Buffer2Ptr pDestBuffer, DRI2Buffer2Ptr pSrcBuffer)
292{
293
294
295    ScreenPtr pScreen = pDraw->pScreen;
296    BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate;
297    BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate;
298    DrawablePtr src_draw;
299    DrawablePtr dst_draw;
300    RegionPtr myClip;
301    GCPtr gc;
302    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
303
304    /*
305     * This is a fragile protection against HW operations when not master.
306     * Needs to be blocked higher up in the dri2 code.
307     */
308    if (!pScrn->vtSema)
309	return;
310
311    /*
312     * In driCreateBuffers we dewrap windows into the
313     * backing pixmaps in order to get to the texture.
314     * We need to use the real drawable in CopyArea
315     * so that cliprects and offsets are correct.
316     */
317    src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
318       &src_priv->pPixmap->drawable;
319    dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
320       &dst_priv->pPixmap->drawable;
321
322    /*
323     * The clients implements glXWaitX with a copy front to fake and then
324     * waiting on the server to signal its completion of it. While
325     * glXWaitGL is a client side flush and a copy from fake to front.
326     * This is how it is done in the DRI2 protocol, how ever depending
327     * which type of drawables the server does things a bit differently
328     * then what the protocol says as the fake and front are the same.
329     *
330     * for pixmaps glXWaitX is a server flush.
331     * for pixmaps glXWaitGL is a client flush.
332     * for windows glXWaitX is a copy from front to fake then a server flush.
333     * for windows glXWaitGL is a client flush then a copy from fake to front.
334     *
335     * XXX in the windows case this code always flushes but that isn't a
336     * must in the glXWaitGL case but we don't know if this is a glXWaitGL
337     * or a glFlush/glFinish call.
338     */
339    if (dst_priv->pPixmap == src_priv->pPixmap) {
340	/* pixmap glXWaitX */
341	if (pSrcBuffer->attachment == DRI2BufferFrontLeft &&
342	    pDestBuffer->attachment == DRI2BufferFakeFrontLeft) {
343
344	    if (!vmwgfx_hw_dri2_validate(dst_priv->pPixmap,
345					 dst_priv->dri2_depth))
346		return;
347	}
348	/* pixmap glXWaitGL */
349	if (pDestBuffer->attachment == DRI2BufferFrontLeft &&
350	    pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) {
351	    return;
352	} else {
353	    vmwgfx_flush_dri2(pScreen);
354	    return;
355	}
356    }
357
358    gc = GetScratchGC(pDraw->depth, pScreen);
359    myClip = REGION_CREATE(pScreen, REGION_RECTS(pRegion),
360			   REGION_NUM_RECTS(pRegion));
361    (*gc->funcs->ChangeClip) (gc, CT_REGION, myClip, 0);
362    ValidateGC(dst_draw, gc);
363
364    /*
365     * Damage the src drawable in order for damageCopyArea to pick up
366     * that something changed.
367     */
368    DamageRegionAppend(src_draw, pRegion);
369    if (pSrcBuffer->attachment != DRI2BufferFrontLeft)
370	saa_drawable_dirty(src_draw, TRUE, pRegion);
371    DamageRegionProcessPending(src_draw);
372
373    /*
374     * Call CopyArea. This usually means a call to damageCopyArea that
375     * is wrapping saa_copy_area. The damageCopyArea function will make
376     * sure the destination drawable is appropriately damaged.
377     */
378    (*gc->ops->CopyArea)(src_draw, dst_draw, gc,
379			 0, 0, pDraw->width, pDraw->height, 0, 0);
380
381    /*
382     * FreeScratchGC will free myClip as well.
383     */
384    myClip = NULL;
385    FreeScratchGC(gc);
386}
387
388#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10)
389static int vmw_dri_auth_magic2(ScreenPtr pScreen, uint32_t magic)
390{
391    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
392    modesettingPtr ms = modesettingPTR(pScrn);
393
394    return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, NULL, magic);
395}
396#endif
397
398#if (DRI2INFOREC_VERSION >= 10)
399static int vmw_dri_auth_magic3(ClientPtr client, ScreenPtr pScreen,
400			       uint32_t magic)
401{
402    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
403    modesettingPtr ms = modesettingPTR(pScrn);
404
405    return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, client, magic);
406}
407#endif
408
409Bool
410xorg_dri2_init(ScreenPtr pScreen)
411{
412    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
413    modesettingPtr ms = modesettingPTR(pScrn);
414    DRI2InfoRec dri2info;
415    int major, minor;
416
417    memset(&dri2info, 0, sizeof(dri2info));
418
419    if (xf86LoaderCheckSymbol("DRI2Version")) {
420	DRI2Version(&major, &minor);
421    } else {
422	/* Assume version 1.0 */
423	major = 1;
424	minor = 0;
425    }
426
427    dri2info.version = min(DRI2INFOREC_VERSION, 3);
428    dri2info.fd = ms->fd;
429    dri2info.driverName = "vmwgfx";
430
431#ifdef VMWGFX_LIBDRM_DEVICENAME
432    ms->dri2_device_name = drmGetDeviceNameFromFd2(ms->fd);
433
434    if (!ms->dri2_device_name) {
435	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
436		   "Could not find the drm device name. Disabling dri2.\n");
437	return FALSE;
438    }
439#else
440    /*
441     * This way of obtaining the DRM device name is a bit
442     * os-specific. Currently this works only for Linux.
443     */
444    {
445	char fdPath[VMWGFX_FD_PATH_LEN];
446	ssize_t numChar;
447
448	memset(fdPath, 0, VMWGFX_FD_PATH_LEN);
449	snprintf(fdPath, VMWGFX_FD_PATH_LEN - 1, "/proc/self/fd/%d", ms->fd);
450	numChar = readlink(fdPath, ms->dri2_device_name, VMWGFX_DRI_DEVICE_LEN);
451	if (numChar <= 0 || numChar >= VMWGFX_DRI_DEVICE_LEN) {
452	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
453		       "Could not find the drm device name. Disabling dri2.\n");
454	    return FALSE;
455	}
456	ms->dri2_device_name[numChar] = 0;
457    }
458#endif
459    dri2info.deviceName = ms->dri2_device_name;
460    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
461	       "Path of drm device is \"%s\".\n", ms->dri2_device_name);
462
463    dri2info.CreateBuffer = dri2_create_buffer;
464    dri2info.DestroyBuffer = dri2_destroy_buffer;
465
466    dri2info.CopyRegion = dri2_copy_region;
467    dri2info.Wait = NULL;
468
469#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10)
470    if (vmwgfx_is_hosted(ms->hdriver)) {
471	dri2info.version = 8;
472	dri2info.AuthMagic2 = vmw_dri_auth_magic2;
473    }
474#endif
475#if (DRI2INFOREC_VERSION >= 10)
476    if (vmwgfx_is_hosted(ms->hdriver)) {
477	dri2info.version = 10;
478	dri2info.AuthMagic3 = vmw_dri_auth_magic3;
479    }
480#endif
481
482    return DRI2ScreenInit(pScreen, &dri2info);
483}
484
485void
486xorg_dri2_close(ScreenPtr pScreen)
487{
488#ifdef VMWGFX_LIBDRM_DEVICENAME
489    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
490    modesettingPtr ms = modesettingPTR(pScrn);
491
492    free(ms->dri2_device_name);
493#endif
494
495    DRI2CloseScreen(pScreen);
496}
497
498/* vim: set sw=4 ts=8 sts=4: */
499