1/*
2 * Copyright 2017 VMWare, Inc.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Author: Thomas Hellstrom <thellstrom@vmware.com>
26 *
27 */
28#ifdef _HAVE_CONFIG_H_
29#include "config.h"
30#endif
31
32#include <xorg-server.h>
33
34#ifdef DRI3
35#include "vmwgfx_driver.h"
36#if (XA_TRACKER_VERSION_MAJOR == VMW_XA_VERSION_MAJOR_DRI3 &&   \
37     XA_TRACKER_VERSION_MINOR >= VMW_XA_VERSION_MINOR_DRI3)
38
39#include "vmwgfx_driver.h"
40#include "vmwgfx_saa_priv.h"
41#include <dri3.h>
42#include <misyncshm.h>
43#include <xf86drm.h>
44#include <unistd.h>
45
46
47/**
48 * \brief DRI3 fd_from_pixmap callback.
49 *
50 */
51static int
52vmwgfx_dri3_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
53                           CARD16 *stride, CARD32 *size)
54{
55    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
56    uint32_t handle;
57    unsigned int byte_stride;
58    ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
59
60    if (!vmwgfx_hw_dri2_validate(pixmap, 0)) {
61        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
62                   "DRI3 pixmap export failed to create HW surface.\n");
63        return -1;
64    }
65
66    if (xa_surface_handle(vpix->hw, xa_handle_type_fd, &handle,
67                          &byte_stride)) {
68        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
69                   "DRI3 pixmap export failed to create handle.\n");
70        return -1;
71    }
72
73    *stride = byte_stride;
74    *size = byte_stride * pixmap->drawable.height;
75
76    /*
77     * hw_is_dri2_fronts will make sure any software rendering to
78     * this pixmap is immediately flushed to the underlying surface.
79     * Strictly, we could wait for glxWaitX to do that, but alas,
80     * even the dri3 glxWaitX appears as broken as the dri2 version.
81     * If we, however, wanted to do that, we'd hook up a shm fence
82     * trigger callback. (Like glamor does).
83     */
84    vpix->hw_is_dri2_fronts = 1;
85
86    return handle;
87}
88
89/**
90 * \brief DRI3 pixmap_from_fd callback.
91 *
92 */
93static PixmapPtr
94vmwgfx_dri3_pixmap_from_fd(ScreenPtr screen, int fd,
95                           CARD16 width, CARD16 height, CARD16 stride,
96                           CARD8 depth, CARD8 bpp)
97{
98    struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(screen));
99    struct xa_surface *srf;
100    struct vmwgfx_saa_pixmap *vpix;
101    ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
102    PixmapPtr pixmap;
103
104    if (width == 0 || height == 0 ||
105        depth < 15 || bpp != BitsPerPixel(depth) || stride < width * bpp / 8)
106        return NULL;
107
108    pixmap = screen->CreatePixmap(screen, width, height, depth, 0);
109    if (!pixmap) {
110        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DRI3 pixmap creation failed.\n");
111        return NULL;
112    }
113
114    vpix = vmwgfx_saa_pixmap(pixmap);
115
116    if (!vmwgfx_hw_dri2_stage(pixmap, depth)) {
117        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
118                   "DRI3 pixmap creation bad format.\n");
119        goto out_bad_format;
120    }
121
122    srf = xa_surface_from_handle2(vsaa->xat, width, height, depth,
123                                  xa_type_other,
124                                  vpix->staging_format,
125                                  vpix->staging_add_flags,
126                                  xa_handle_type_fd,
127                                  fd, stride);
128    if (!srf) {
129        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
130                   "DRI3 pixmap creation surface sharing failed.\n");
131        goto out_bad_format;
132    }
133
134    vpix->xa_flags = vpix->staging_add_flags;
135    vpix->hw = srf;
136    if (!vmwgfx_create_hw(vsaa, pixmap, TRUE)) {
137        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
138                   "DRI3 pixmap creation failed SAA enabling.\n");
139        goto out_no_damage;
140    }
141
142    vpix->hw_is_dri2_fronts = 1;
143    return pixmap;
144
145  out_no_damage:
146    xa_surface_unref(srf);
147  out_bad_format:
148    screen->DestroyPixmap(pixmap);
149
150    return NULL;
151}
152
153/**
154 * \brief Open a render node.
155 *
156 * \param screen[IN]  Pointer to the screen
157 * \return  A valid file descriptor or -1 on failure.
158 */
159static int
160vmwgfx_dri3_open_render(ScreenPtr screen)
161{
162    ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
163    modesettingPtr ms = modesettingPTR(pScrn);
164    char bus_id[64];
165    int fd;
166
167    snprintf(bus_id, sizeof(bus_id), "PCI:%d:%d:%d",
168             ((ms->PciInfo->domain << 8) | ms->PciInfo->bus),
169             ms->PciInfo->dev, ms->PciInfo->func);
170
171    /* Render nodes can't be opened by busid yet.. */
172    fd = drmOpenWithType("vmwgfx", bus_id, DRM_NODE_RENDER);
173    if (fd < 0)
174        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
175                   "DRI3 client open busid \"%s\" failed.\n", bus_id);
176
177    return fd;
178}
179
180/**
181 * \brief DRI3 open_client callback.
182 *
183 */
184static int
185vmwgfx_dri3_open_client(ClientPtr client, ScreenPtr screen,
186                        RRProviderPtr provider, int *pfd)
187{
188    *pfd = vmwgfx_dri3_open_render(screen);
189
190    return (*pfd >= 0) ? Success : BadAlloc;
191}
192
193/**
194 * \brief Verify that surface sharing between render client and X server
195 * works.
196 *
197 * \param screen[IN,OUT]  A pointer to the current screen.
198 * \return TRUE if successful, FALSE otherwise.
199 *
200 * Opens a render client, creates a surface and tries to share that surface
201 * with the X server. There is a vmwgfx kernel driver bug that, combined
202 * with a pre-guest-backed-surface svga mesa driver bug,
203 * prevents this sharing to happen and thus breaks dri3.
204 *
205 * Also, we need to make sure that we can share an XRGB surface as an
206 * ARGB surface since DRI3 does not appear to be as strict about internal
207 * surface formats as DRI2.
208 */
209static Bool
210vmwgfx_dri3_verify_sharing(ScreenPtr screen)
211{
212    ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
213    modesettingPtr ms = modesettingPTR(pScrn);
214    int fd = vmwgfx_dri3_open_render(screen);
215    struct xa_tracker *xat;
216    struct xa_surface *srf1;
217    unsigned int stride;
218    uint32_t handle;
219    Bool ret = FALSE;
220
221    if (fd < 0)
222        return FALSE;
223
224    xat = xa_tracker_create(fd);
225    if (!xat)
226        goto out_no_xa;
227
228    /* Here we're the render client (xat) */
229    srf1 = xa_surface_create(xat, 16, 16, 32, xa_type_argb,
230                             xa_format_unknown,
231                             XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED);
232    if (!srf1)
233        goto out_no_surface;
234
235    if (xa_surface_handle(srf1, xa_handle_type_fd, &handle, &stride) !=
236        XA_ERR_NONE)
237        goto out_no_handle;
238
239    xa_surface_unref(srf1);
240
241    /* Now we're the X server (ms->xat) */
242    srf1 = xa_surface_from_handle2(ms->xat, 16, 16, 24, xa_type_argb,
243                                   xa_format_unknown,
244                                   XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED,
245                                   xa_handle_type_fd, handle, stride);
246    if (!srf1)
247        goto out_no_surface;
248
249    ret = TRUE;
250    close(handle);
251
252  out_no_handle:
253    xa_surface_unref(srf1);
254  out_no_surface:
255    xa_tracker_destroy(xat);
256  out_no_xa:
257    close(fd);
258
259    return ret;
260}
261
262static dri3_screen_info_rec vmwgfx_dri3_info = {
263    .version = 1,
264    .open = NULL,
265    .pixmap_from_fd = vmwgfx_dri3_pixmap_from_fd,
266    .fd_from_pixmap = vmwgfx_dri3_fd_from_pixmap,
267    .open_client = vmwgfx_dri3_open_client,
268};
269
270
271/**
272 * \brief Initialize dri3.
273 *
274 * \param screen[IN,OUT]  A pointer to the current screen.
275 * \return TRUE if successful, FALSE otherwise.
276 */
277Bool
278vmwgfx_dri3_init(ScreenPtr screen)
279{
280    ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
281
282    if (!vmwgfx_dri3_verify_sharing(screen)) {
283        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
284                   "Failed to verify XA surface sharing for DRI3.\n");
285        return FALSE;
286    }
287
288    if (!miSyncShmScreenInit(screen)) {
289        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
290                   "Failed to initialize xshm sync for DRI3.\n");
291        return FALSE;
292    }
293
294    if (!dri3_screen_init(screen, &vmwgfx_dri3_info)) {
295        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to initialize DRI3.\n");
296        return FALSE;
297    }
298
299    return TRUE;
300}
301
302#else /* XA INCLUDES SUFFICIENT */
303Bool
304vmwgfx_dri3_init(ScreenPtr screen)
305{
306    return FALSE;
307}
308
309#endif /* !XA INCLUDES SUFFICIENT */
310#endif /* DRI3 */
311