1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "dri3_priv.h"
24#include <syncsdk.h>
25#include <misync.h>
26#include <misyncshm.h>
27#include <randrstr.h>
28#include <drm_fourcc.h>
29#include <unistd.h>
30
31int
32dri3_open(ClientPtr client, ScreenPtr screen, RRProviderPtr provider, int *fd)
33{
34    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
35    const dri3_screen_info_rec *info = ds->info;
36
37    if (info == NULL)
38        return BadMatch;
39
40    if (info->version >= 1 && info->open_client != NULL)
41        return (*info->open_client) (client, screen, provider, fd);
42    if (info->open != NULL)
43        return (*info->open) (screen, provider, fd);
44
45    return BadMatch;
46}
47
48int
49dri3_pixmap_from_fds(PixmapPtr *ppixmap, ScreenPtr screen,
50                     CARD8 num_fds, const int *fds,
51                     CARD16 width, CARD16 height,
52                     const CARD32 *strides, const CARD32 *offsets,
53                     CARD8 depth, CARD8 bpp, CARD64 modifier)
54{
55    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
56    const dri3_screen_info_rec *info = ds->info;
57    PixmapPtr                   pixmap;
58
59    if (!info)
60        return BadImplementation;
61
62    if (info->version >= 2 && info->pixmap_from_fds != NULL) {
63        pixmap = (*info->pixmap_from_fds) (screen, num_fds, fds, width, height,
64                                           strides, offsets, depth, bpp, modifier);
65    } else if (info->pixmap_from_fd != NULL && num_fds == 1) {
66        pixmap = (*info->pixmap_from_fd) (screen, fds[0], width, height,
67                                          strides[0], depth, bpp);
68    } else {
69        return BadImplementation;
70    }
71
72    if (!pixmap)
73        return BadAlloc;
74
75    *ppixmap = pixmap;
76    return Success;
77}
78
79int
80dri3_fds_from_pixmap(PixmapPtr pixmap, int *fds,
81                     uint32_t *strides, uint32_t *offsets,
82                     uint64_t *modifier)
83{
84    ScreenPtr                   screen = pixmap->drawable.pScreen;
85    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
86    const dri3_screen_info_rec *info = ds->info;
87
88    if (!info)
89        return 0;
90
91    if (info->version >= 2 && info->fds_from_pixmap != NULL) {
92        return (*info->fds_from_pixmap)(screen, pixmap, fds, strides, offsets,
93                                        modifier);
94    } else if (info->fd_from_pixmap != NULL) {
95        CARD16 stride;
96        CARD32 size;
97
98        fds[0] = (*info->fd_from_pixmap)(screen, pixmap, &stride, &size);
99        if (fds[0] < 0)
100            return 0;
101
102        strides[0] = stride;
103        offsets[0] = 0;
104        *modifier = DRM_FORMAT_MOD_INVALID;
105        return 1;
106    } else {
107        return 0;
108    }
109}
110
111int
112dri3_fd_from_pixmap(PixmapPtr pixmap, CARD16 *stride, CARD32 *size)
113{
114    ScreenPtr                   screen = pixmap->drawable.pScreen;
115    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
116    const dri3_screen_info_rec  *info = ds->info;
117    uint32_t                    strides[4];
118    uint32_t                    offsets[4];
119    uint64_t                    modifier;
120    int                         fds[4];
121    int                         num_fds;
122
123    if (!info)
124        return -1;
125
126    /* Preferentially use the old interface, allowing the implementation to
127     * ensure the buffer is in a single-plane format which doesn't need
128     * modifiers. */
129    if (info->fd_from_pixmap != NULL)
130        return (*info->fd_from_pixmap)(screen, pixmap, stride, size);
131
132    if (info->version < 2 || info->fds_from_pixmap == NULL)
133        return -1;
134
135    /* If using the new interface, make sure that it's a single plane starting
136     * at 0 within the BO. We don't check the modifier, as the client may
137     * have an auxiliary mechanism for determining the modifier itself. */
138    num_fds = info->fds_from_pixmap(screen, pixmap, fds, strides, offsets,
139                                    &modifier);
140    if (num_fds != 1 || offsets[0] != 0) {
141        int i;
142        for (i = 0; i < num_fds; i++)
143            close(fds[i]);
144        return -1;
145    }
146
147    *stride = strides[0];
148    *size = size[0];
149    return fds[0];
150}
151
152static int
153cache_formats_and_modifiers(ScreenPtr screen)
154{
155    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
156    const dri3_screen_info_rec *info = ds->info;
157    CARD32                      num_formats;
158    CARD32                     *formats;
159    uint32_t                    num_modifiers;
160    uint64_t                   *modifiers;
161    int                         i;
162
163    if (ds->formats_cached)
164        return Success;
165
166    if (!info)
167        return BadImplementation;
168
169    if (info->version < 2 || !info->get_formats || !info->get_modifiers) {
170        ds->formats = NULL;
171        ds->num_formats = 0;
172        ds->formats_cached = TRUE;
173        return Success;
174    }
175
176    if (!info->get_formats(screen, &num_formats, &formats))
177        return BadAlloc;
178
179    if (!num_formats) {
180        ds->num_formats = 0;
181        ds->formats_cached = TRUE;
182        return Success;
183    }
184
185    ds->formats = calloc(num_formats, sizeof(dri3_dmabuf_format_rec));
186    if (!ds->formats)
187        return BadAlloc;
188
189    for (i = 0; i < num_formats; i++) {
190        dri3_dmabuf_format_ptr iter = &ds->formats[i];
191
192        if (!info->get_modifiers(screen, formats[i],
193                                 &num_modifiers,
194                                 &modifiers))
195            continue;
196
197        if (!num_modifiers)
198            continue;
199
200        iter->format = formats[i];
201        iter->num_modifiers = num_modifiers;
202        iter->modifiers = modifiers;
203    }
204
205    ds->num_formats = i;
206    ds->formats_cached = TRUE;
207
208    return Success;
209}
210
211int
212dri3_get_supported_modifiers(ScreenPtr screen, DrawablePtr drawable,
213                             CARD8 depth, CARD8 bpp,
214                             CARD32 *num_intersect_modifiers,
215                             CARD64 **intersect_modifiers,
216                             CARD32 *num_screen_modifiers,
217                             CARD64 **screen_modifiers)
218{
219    dri3_screen_priv_ptr        ds = dri3_screen_priv(screen);
220    const dri3_screen_info_rec *info = ds->info;
221    int                         i, j;
222    int                         ret;
223    uint32_t                    num_drawable_mods;
224    uint64_t                   *drawable_mods;
225    CARD64                     *intersect_mods = NULL;
226    CARD64                     *screen_mods = NULL;
227    CARD32                      format;
228    dri3_dmabuf_format_ptr      screen_format = NULL;
229
230    ret = cache_formats_and_modifiers(screen);
231    if (ret != Success)
232        return ret;
233
234    format = drm_format_for_depth(depth, bpp);
235    if (format == 0)
236        return BadValue;
237
238    /* Find screen-global modifiers from cache
239     */
240    for (i = 0; i < ds->num_formats; i++) {
241        if (ds->formats[i].format == format) {
242            screen_format = &ds->formats[i];
243            break;
244        }
245    }
246    if (screen_format == NULL)
247        return BadMatch;
248
249    if (screen_format->num_modifiers == 0) {
250        *num_screen_modifiers = 0;
251        *num_intersect_modifiers = 0;
252        return Success;
253    }
254
255    if (!info->get_drawable_modifiers ||
256        !info->get_drawable_modifiers(drawable, format,
257                                      &num_drawable_mods,
258                                      &drawable_mods)) {
259        num_drawable_mods = 0;
260        drawable_mods = NULL;
261    }
262
263    /* We're allocating slightly more memory than necessary but it reduces
264     * the complexity of finding the intersection set.
265     */
266    screen_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
267    if (!screen_mods)
268        return BadAlloc;
269    if (num_drawable_mods > 0) {
270        intersect_mods = malloc(screen_format->num_modifiers * sizeof(CARD64));
271        if (!intersect_mods) {
272            free(screen_mods);
273            return BadAlloc;
274        }
275    }
276
277    *num_screen_modifiers = 0;
278    *num_intersect_modifiers = 0;
279    for (i = 0; i < screen_format->num_modifiers; i++) {
280        CARD64 modifier = screen_format->modifiers[i];
281        Bool intersect = FALSE;
282
283        for (j = 0; j < num_drawable_mods; j++) {
284            if (drawable_mods[j] == modifier) {
285                intersect = TRUE;
286                break;
287            }
288        }
289
290        if (intersect) {
291            intersect_mods[*num_intersect_modifiers] = modifier;
292            *num_intersect_modifiers += 1;
293        } else {
294            screen_mods[*num_screen_modifiers] = modifier;
295            *num_screen_modifiers += 1;
296        }
297    }
298
299    assert(*num_intersect_modifiers + *num_screen_modifiers == screen_format->num_modifiers);
300
301    *intersect_modifiers = intersect_mods;
302    *screen_modifiers = screen_mods;
303    free(drawable_mods);
304
305    return Success;
306}
307