1/*
2 * Copyright © 2007 Red Hat, Inc.
3 * Copyright © 2019 NVIDIA CORPORATION
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 *    Dave Airlie <airlied@redhat.com>
26 *    Aaron Plattner <aplattner@nvidia.com>
27 *
28 */
29
30#ifdef HAVE_DIX_CONFIG_H
31#include "dix-config.h"
32#endif
33
34#include <errno.h>
35#include <sys/ioctl.h>
36#include <sys/mman.h>
37#include <unistd.h>
38#include "dumb_bo.h"
39#include "inputstr.h"
40#include "xf86str.h"
41#include "X11/Xatom.h"
42#include "mi.h"
43#include "micmap.h"
44#include "xf86cmap.h"
45#include "xf86DDC.h"
46#include <drm_fourcc.h>
47#include <drm_mode.h>
48
49#include <xf86drm.h>
50#include "xf86Crtc.h"
51#include "drmmode_display.h"
52#include "present.h"
53
54#include <cursorstr.h>
55
56#include <X11/extensions/dpmsconst.h>
57
58#include "driver.h"
59
60static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
61static PixmapPtr drmmode_create_pixmap_header(ScreenPtr pScreen, int width, int height,
62                                              int depth, int bitsPerPixel, int devKind,
63                                              void *pPixData);
64
65static const struct drm_color_ctm ctm_identity = { {
66    1ULL << 32, 0, 0,
67    0, 1ULL << 32, 0,
68    0, 0, 1ULL << 32
69} };
70
71static Bool ctm_is_identity(const struct drm_color_ctm *ctm)
72{
73    const size_t matrix_len = sizeof(ctm->matrix) / sizeof(ctm->matrix[0]);
74    const uint64_t one = 1ULL << 32;
75    const uint64_t neg_zero = 1ULL << 63;
76    int i;
77
78    for (i = 0; i < matrix_len; i++) {
79        const Bool diagonal = i / 3 == i % 3;
80        const uint64_t val = ctm->matrix[i];
81
82        if ((diagonal && val != one) ||
83            (!diagonal && val != 0 && val != neg_zero)) {
84            return FALSE;
85        }
86    }
87
88    return TRUE;
89}
90
91static inline uint32_t *
92formats_ptr(struct drm_format_modifier_blob *blob)
93{
94    return (uint32_t *)(((char *)blob) + blob->formats_offset);
95}
96
97static inline struct drm_format_modifier *
98modifiers_ptr(struct drm_format_modifier_blob *blob)
99{
100    return (struct drm_format_modifier *)(((char *)blob) + blob->modifiers_offset);
101}
102
103static uint32_t
104get_opaque_format(uint32_t format)
105{
106    switch (format) {
107    case DRM_FORMAT_ARGB8888:
108        return DRM_FORMAT_XRGB8888;
109    case DRM_FORMAT_ARGB2101010:
110        return DRM_FORMAT_XRGB2101010;
111    default:
112        return format;
113    }
114}
115
116Bool
117drmmode_is_format_supported(ScrnInfoPtr scrn, uint32_t format, uint64_t modifier)
118{
119    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
120    int c, i, j;
121
122    /* BO are imported as opaque surface, so let's pretend there is no alpha */
123    format = get_opaque_format(format);
124
125    for (c = 0; c < xf86_config->num_crtc; c++) {
126        xf86CrtcPtr crtc = xf86_config->crtc[c];
127        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
128        Bool found = FALSE;
129
130        if (!crtc->enabled)
131            continue;
132
133        if (drmmode_crtc->num_formats == 0)
134            continue;
135
136        for (i = 0; i < drmmode_crtc->num_formats; i++) {
137            drmmode_format_ptr iter = &drmmode_crtc->formats[i];
138
139            if (iter->format != format)
140                continue;
141
142            if (modifier == DRM_FORMAT_MOD_INVALID ||
143                iter->num_modifiers == 0) {
144                found = TRUE;
145                break;
146            }
147
148            for (j = 0; j < iter->num_modifiers; j++) {
149                if (iter->modifiers[j] == modifier) {
150                    found = TRUE;
151                    break;
152                }
153            }
154
155            break;
156        }
157
158        if (!found)
159            return FALSE;
160    }
161
162    return TRUE;
163}
164
165#ifdef GBM_BO_WITH_MODIFIERS
166static uint32_t
167get_modifiers_set(ScrnInfoPtr scrn, uint32_t format, uint64_t **modifiers,
168                  Bool enabled_crtc_only, Bool exclude_multiplane)
169{
170    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
171    modesettingPtr ms = modesettingPTR(scrn);
172    drmmode_ptr drmmode = &ms->drmmode;
173    int c, i, j, k, count_modifiers = 0;
174    uint64_t *tmp, *ret = NULL;
175
176    /* BOs are imported as opaque surfaces, so pretend the same thing here */
177    format = get_opaque_format(format);
178
179    *modifiers = NULL;
180    for (c = 0; c < xf86_config->num_crtc; c++) {
181        xf86CrtcPtr crtc = xf86_config->crtc[c];
182        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
183
184        if (enabled_crtc_only && !crtc->enabled)
185            continue;
186
187        for (i = 0; i < drmmode_crtc->num_formats; i++) {
188            drmmode_format_ptr iter = &drmmode_crtc->formats[i];
189
190            if (iter->format != format)
191                continue;
192
193            for (j = 0; j < iter->num_modifiers; j++) {
194                Bool found = FALSE;
195
196		/* Don't choose multi-plane formats for our screen pixmap.
197		 * These will get used with frontbuffer rendering, which will
198		 * lead to worse-than-tearing with multi-plane formats, as the
199		 * primary and auxiliary planes go out of sync. */
200		if (exclude_multiplane &&
201                    gbm_device_get_format_modifier_plane_count(drmmode->gbm,
202                                                               format,
203                                                               iter->modifiers[j]) > 1) {
204                    continue;
205                }
206
207                for (k = 0; k < count_modifiers; k++) {
208                    if (iter->modifiers[j] == ret[k])
209                        found = TRUE;
210                }
211                if (!found) {
212                    count_modifiers++;
213                    tmp = realloc(ret, count_modifiers * sizeof(uint64_t));
214                    if (!tmp) {
215                        free(ret);
216                        return 0;
217                    }
218                    ret = tmp;
219                    ret[count_modifiers - 1] = iter->modifiers[j];
220                }
221            }
222        }
223    }
224
225    *modifiers = ret;
226    return count_modifiers;
227}
228
229static Bool
230get_drawable_modifiers(DrawablePtr draw, uint32_t format,
231                       uint32_t *num_modifiers, uint64_t **modifiers)
232{
233    ScrnInfoPtr scrn = xf86ScreenToScrn(draw->pScreen);
234    modesettingPtr ms = modesettingPTR(scrn);
235
236    if (!present_can_window_flip((WindowPtr) draw) ||
237        !ms->drmmode.pageflip || ms->drmmode.dri2_flipping || !scrn->vtSema) {
238        *num_modifiers = 0;
239        *modifiers = NULL;
240        return TRUE;
241    }
242
243    *num_modifiers = get_modifiers_set(scrn, format, modifiers, TRUE, FALSE);
244    return TRUE;
245}
246#endif
247
248static Bool
249drmmode_zaphod_string_matches(ScrnInfoPtr scrn, const char *s, char *output_name)
250{
251    char **token = xstrtokenize(s, ", \t\n\r");
252    Bool ret = FALSE;
253
254    if (!token)
255        return FALSE;
256
257    for (int i = 0; token[i]; i++) {
258        if (strcmp(token[i], output_name) == 0)
259            ret = TRUE;
260
261        free(token[i]);
262    }
263
264    free(token);
265
266    return ret;
267}
268
269static uint64_t
270drmmode_prop_get_value(drmmode_prop_info_ptr info,
271                       drmModeObjectPropertiesPtr props,
272                       uint64_t def)
273{
274    unsigned int i;
275
276    if (info->prop_id == 0)
277        return def;
278
279    for (i = 0; i < props->count_props; i++) {
280        unsigned int j;
281
282        if (props->props[i] != info->prop_id)
283            continue;
284
285        /* Simple (non-enum) types can return the value directly */
286        if (info->num_enum_values == 0)
287            return props->prop_values[i];
288
289        /* Map from raw value to enum value */
290        for (j = 0; j < info->num_enum_values; j++) {
291            if (!info->enum_values[j].valid)
292                continue;
293            if (info->enum_values[j].value != props->prop_values[i])
294                continue;
295
296            return j;
297        }
298    }
299
300    return def;
301}
302
303static uint32_t
304drmmode_prop_info_update(drmmode_ptr drmmode,
305                         drmmode_prop_info_ptr info,
306                         unsigned int num_infos,
307                         drmModeObjectProperties *props)
308{
309    drmModePropertyRes *prop;
310    uint32_t valid_mask = 0;
311    unsigned i, j;
312
313    assert(num_infos <= 32 && "update return type");
314
315    for (i = 0; i < props->count_props; i++) {
316        Bool props_incomplete = FALSE;
317        unsigned int k;
318
319        for (j = 0; j < num_infos; j++) {
320            if (info[j].prop_id == props->props[i])
321                break;
322            if (!info[j].prop_id)
323                props_incomplete = TRUE;
324        }
325
326        /* We've already discovered this property. */
327        if (j != num_infos)
328            continue;
329
330        /* We haven't found this property ID, but as we've already
331         * found all known properties, we don't need to look any
332         * further. */
333        if (!props_incomplete)
334            break;
335
336        prop = drmModeGetProperty(drmmode->fd, props->props[i]);
337        if (!prop)
338            continue;
339
340        for (j = 0; j < num_infos; j++) {
341            if (!strcmp(prop->name, info[j].name))
342                break;
343        }
344
345        /* We don't know/care about this property. */
346        if (j == num_infos) {
347            drmModeFreeProperty(prop);
348            continue;
349        }
350
351        info[j].prop_id = props->props[i];
352        info[j].value = props->prop_values[i];
353        valid_mask |= 1U << j;
354
355        if (info[j].num_enum_values == 0) {
356            drmModeFreeProperty(prop);
357            continue;
358        }
359
360        if (!(prop->flags & DRM_MODE_PROP_ENUM)) {
361            xf86DrvMsg(drmmode->scrn->scrnIndex, X_WARNING,
362                       "expected property %s to be an enum,"
363                       " but it is not; ignoring\n", prop->name);
364            drmModeFreeProperty(prop);
365            continue;
366        }
367
368        for (k = 0; k < info[j].num_enum_values; k++) {
369            int l;
370
371            if (info[j].enum_values[k].valid)
372                continue;
373
374            for (l = 0; l < prop->count_enums; l++) {
375                if (!strcmp(prop->enums[l].name,
376                            info[j].enum_values[k].name))
377                    break;
378            }
379
380            if (l == prop->count_enums)
381                continue;
382
383            info[j].enum_values[k].valid = TRUE;
384            info[j].enum_values[k].value = prop->enums[l].value;
385        }
386
387        drmModeFreeProperty(prop);
388    }
389
390    return valid_mask;
391}
392
393static Bool
394drmmode_prop_info_copy(drmmode_prop_info_ptr dst,
395		       const drmmode_prop_info_rec *src,
396		       unsigned int num_props,
397		       Bool copy_prop_id)
398{
399    unsigned int i;
400
401    memcpy(dst, src, num_props * sizeof(*dst));
402
403    for (i = 0; i < num_props; i++) {
404        unsigned int j;
405
406        if (copy_prop_id)
407            dst[i].prop_id = src[i].prop_id;
408        else
409            dst[i].prop_id = 0;
410
411        if (src[i].num_enum_values == 0)
412            continue;
413
414        dst[i].enum_values =
415            malloc(src[i].num_enum_values *
416                    sizeof(*dst[i].enum_values));
417        if (!dst[i].enum_values)
418            goto err;
419
420        memcpy(dst[i].enum_values, src[i].enum_values,
421                src[i].num_enum_values * sizeof(*dst[i].enum_values));
422
423        for (j = 0; j < dst[i].num_enum_values; j++)
424            dst[i].enum_values[j].valid = FALSE;
425    }
426
427    return TRUE;
428
429err:
430    while (i--)
431        free(dst[i].enum_values);
432    return FALSE;
433}
434
435static void
436drmmode_prop_info_free(drmmode_prop_info_ptr info, int num_props)
437{
438    int i;
439
440    for (i = 0; i < num_props; i++)
441        free(info[i].enum_values);
442}
443
444static void
445drmmode_ConvertToKMode(ScrnInfoPtr scrn,
446                       drmModeModeInfo * kmode, DisplayModePtr mode);
447
448
449static int
450plane_add_prop(drmModeAtomicReq *req, drmmode_crtc_private_ptr drmmode_crtc,
451               enum drmmode_plane_property prop, uint64_t val)
452{
453    drmmode_prop_info_ptr info = &drmmode_crtc->props_plane[prop];
454    int ret;
455
456    if (!info)
457        return -1;
458
459    ret = drmModeAtomicAddProperty(req, drmmode_crtc->plane_id,
460                                   info->prop_id, val);
461    return (ret <= 0) ? -1 : 0;
462}
463
464static int
465plane_add_props(drmModeAtomicReq *req, xf86CrtcPtr crtc,
466                uint32_t fb_id, int x, int y)
467{
468    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
469    int ret = 0;
470
471    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_FB_ID,
472                          fb_id);
473    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_ID,
474                          fb_id ? drmmode_crtc->mode_crtc->crtc_id : 0);
475    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_X, x << 16);
476    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_Y, y << 16);
477    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_W,
478                          crtc->mode.HDisplay << 16);
479    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_SRC_H,
480                          crtc->mode.VDisplay << 16);
481    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_X, 0);
482    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_Y, 0);
483    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_W,
484                          crtc->mode.HDisplay);
485    ret |= plane_add_prop(req, drmmode_crtc, DRMMODE_PLANE_CRTC_H,
486                          crtc->mode.VDisplay);
487
488    return ret;
489}
490
491static int
492crtc_add_prop(drmModeAtomicReq *req, drmmode_crtc_private_ptr drmmode_crtc,
493              enum drmmode_crtc_property prop, uint64_t val)
494{
495    drmmode_prop_info_ptr info = &drmmode_crtc->props[prop];
496    int ret;
497
498    if (!info)
499        return -1;
500
501    ret = drmModeAtomicAddProperty(req, drmmode_crtc->mode_crtc->crtc_id,
502                                   info->prop_id, val);
503    return (ret <= 0) ? -1 : 0;
504}
505
506static int
507connector_add_prop(drmModeAtomicReq *req, drmmode_output_private_ptr drmmode_output,
508                   enum drmmode_connector_property prop, uint64_t val)
509{
510    drmmode_prop_info_ptr info = &drmmode_output->props_connector[prop];
511    int ret;
512
513    if (!info)
514        return -1;
515
516    ret = drmModeAtomicAddProperty(req, drmmode_output->output_id,
517                                   info->prop_id, val);
518    return (ret <= 0) ? -1 : 0;
519}
520
521static int
522drmmode_CompareKModes(drmModeModeInfo * kmode, drmModeModeInfo * other)
523{
524    return memcmp(kmode, other, sizeof(*kmode));
525}
526
527static int
528drm_mode_ensure_blob(xf86CrtcPtr crtc, drmModeModeInfo mode_info)
529{
530    modesettingPtr ms = modesettingPTR(crtc->scrn);
531    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
532    drmmode_mode_ptr mode;
533    int ret;
534
535    if (drmmode_crtc->current_mode &&
536        drmmode_CompareKModes(&drmmode_crtc->current_mode->mode_info, &mode_info) == 0)
537        return 0;
538
539    mode = calloc(sizeof(drmmode_mode_rec), 1);
540    if (!mode)
541        return -1;
542
543    mode->mode_info = mode_info;
544    ret = drmModeCreatePropertyBlob(ms->fd,
545                                    &mode->mode_info,
546                                    sizeof(mode->mode_info),
547                                    &mode->blob_id);
548    drmmode_crtc->current_mode = mode;
549    xorg_list_add(&mode->entry, &drmmode_crtc->mode_list);
550
551    return ret;
552}
553
554static int
555crtc_add_dpms_props(drmModeAtomicReq *req, xf86CrtcPtr crtc,
556                    int new_dpms, Bool *active)
557{
558    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
559    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
560    Bool crtc_active = FALSE;
561    int i;
562    int ret = 0;
563
564    for (i = 0; i < xf86_config->num_output; i++) {
565        xf86OutputPtr output = xf86_config->output[i];
566        drmmode_output_private_ptr drmmode_output = output->driver_private;
567
568        if (output->crtc != crtc) {
569            if (drmmode_output->current_crtc == crtc) {
570                ret |= connector_add_prop(req, drmmode_output,
571                                          DRMMODE_CONNECTOR_CRTC_ID, 0);
572            }
573            continue;
574        }
575
576        if (drmmode_output->output_id == -1)
577            continue;
578
579        if (new_dpms == DPMSModeOn)
580            crtc_active = TRUE;
581
582        ret |= connector_add_prop(req, drmmode_output,
583                                  DRMMODE_CONNECTOR_CRTC_ID,
584                                  crtc_active ?
585                                      drmmode_crtc->mode_crtc->crtc_id : 0);
586    }
587
588    if (crtc_active) {
589        drmModeModeInfo kmode;
590
591        drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
592        ret |= drm_mode_ensure_blob(crtc, kmode);
593
594        ret |= crtc_add_prop(req, drmmode_crtc,
595                             DRMMODE_CRTC_ACTIVE, 1);
596        ret |= crtc_add_prop(req, drmmode_crtc,
597                             DRMMODE_CRTC_MODE_ID,
598                             drmmode_crtc->current_mode->blob_id);
599    } else {
600        ret |= crtc_add_prop(req, drmmode_crtc,
601                             DRMMODE_CRTC_ACTIVE, 0);
602        ret |= crtc_add_prop(req, drmmode_crtc,
603                             DRMMODE_CRTC_MODE_ID, 0);
604    }
605
606    if (active)
607        *active = crtc_active;
608
609    return ret;
610}
611
612static void
613drm_mode_destroy(xf86CrtcPtr crtc, drmmode_mode_ptr mode)
614{
615    modesettingPtr ms = modesettingPTR(crtc->scrn);
616    if (mode->blob_id)
617        drmModeDestroyPropertyBlob(ms->fd, mode->blob_id);
618    xorg_list_del(&mode->entry);
619    free(mode);
620}
621
622static int
623drmmode_crtc_can_test_mode(xf86CrtcPtr crtc)
624{
625    modesettingPtr ms = modesettingPTR(crtc->scrn);
626
627    return ms->atomic_modeset;
628}
629
630Bool
631drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y)
632{
633    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
634    drmmode_ptr drmmode = drmmode_crtc->drmmode;
635    int ret;
636
637    *fb_id = 0;
638
639    if (drmmode_crtc->prime_pixmap) {
640        if (!drmmode->reverse_prime_offload_mode) {
641            msPixmapPrivPtr ppriv =
642                msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
643            *fb_id = ppriv->fb_id;
644            *x = 0;
645        } else
646            *x = drmmode_crtc->prime_pixmap_x;
647        *y = 0;
648    }
649    else if (drmmode_crtc->rotate_fb_id) {
650        *fb_id = drmmode_crtc->rotate_fb_id;
651        *x = *y = 0;
652    }
653    else {
654        *fb_id = drmmode->fb_id;
655        *x = crtc->x;
656        *y = crtc->y;
657    }
658
659    if (*fb_id == 0) {
660        ret = drmmode_bo_import(drmmode, &drmmode->front_bo,
661                                &drmmode->fb_id);
662        if (ret < 0) {
663            ErrorF("failed to add fb %d\n", ret);
664            return FALSE;
665        }
666        *fb_id = drmmode->fb_id;
667    }
668
669    return TRUE;
670}
671
672void
673drmmode_set_dpms(ScrnInfoPtr scrn, int dpms, int flags)
674{
675    modesettingPtr ms = modesettingPTR(scrn);
676    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
677    drmModeAtomicReq *req = drmModeAtomicAlloc();
678    uint32_t mode_flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
679    int ret = 0;
680    int i;
681
682    assert(ms->atomic_modeset);
683
684    if (!req)
685        return;
686
687    for (i = 0; i < xf86_config->num_output; i++) {
688        xf86OutputPtr output = xf86_config->output[i];
689        drmmode_output_private_ptr drmmode_output = output->driver_private;
690
691        if (output->crtc != NULL)
692            continue;
693
694        ret = connector_add_prop(req, drmmode_output,
695                                 DRMMODE_CONNECTOR_CRTC_ID, 0);
696    }
697
698    for (i = 0; i < xf86_config->num_crtc; i++) {
699        xf86CrtcPtr crtc = xf86_config->crtc[i];
700        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
701        Bool active = FALSE;
702
703        ret |= crtc_add_dpms_props(req, crtc, dpms, &active);
704
705        if (dpms == DPMSModeOn && active && drmmode_crtc->need_modeset) {
706            uint32_t fb_id;
707            int x, y;
708
709            if (!drmmode_crtc_get_fb_id(crtc, &fb_id, &x, &y))
710                continue;
711            ret |= plane_add_props(req, crtc, fb_id, x, y);
712            drmmode_crtc->need_modeset = FALSE;
713        }
714    }
715
716    if (ret == 0)
717        drmModeAtomicCommit(ms->fd, req, mode_flags, NULL);
718    drmModeAtomicFree(req);
719
720    ms->pending_modeset = TRUE;
721    xf86DPMSSet(scrn, dpms, flags);
722    ms->pending_modeset = FALSE;
723}
724
725static int
726drmmode_output_disable(xf86OutputPtr output)
727{
728    modesettingPtr ms = modesettingPTR(output->scrn);
729    drmmode_output_private_ptr drmmode_output = output->driver_private;
730    xf86CrtcPtr crtc = drmmode_output->current_crtc;
731    drmModeAtomicReq *req = drmModeAtomicAlloc();
732    uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
733    int ret = 0;
734
735    assert(ms->atomic_modeset);
736
737    if (!req)
738        return 1;
739
740    ret |= connector_add_prop(req, drmmode_output,
741                              DRMMODE_CONNECTOR_CRTC_ID, 0);
742    if (crtc)
743        ret |= crtc_add_dpms_props(req, crtc, DPMSModeOff, NULL);
744
745    if (ret == 0)
746        ret = drmModeAtomicCommit(ms->fd, req, flags, NULL);
747
748    if (ret == 0)
749        drmmode_output->current_crtc = NULL;
750
751    drmModeAtomicFree(req);
752    return ret;
753}
754
755static int
756drmmode_crtc_disable(xf86CrtcPtr crtc)
757{
758    modesettingPtr ms = modesettingPTR(crtc->scrn);
759    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
760    drmModeAtomicReq *req = drmModeAtomicAlloc();
761    uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
762    int ret = 0;
763
764    assert(ms->atomic_modeset);
765
766    if (!req)
767        return 1;
768
769    ret |= crtc_add_prop(req, drmmode_crtc,
770                         DRMMODE_CRTC_ACTIVE, 0);
771    ret |= crtc_add_prop(req, drmmode_crtc,
772                         DRMMODE_CRTC_MODE_ID, 0);
773
774    if (ret == 0)
775        ret = drmModeAtomicCommit(ms->fd, req, flags, NULL);
776
777    drmModeAtomicFree(req);
778    return ret;
779}
780
781static void
782drmmode_set_ctm(xf86CrtcPtr crtc, const struct drm_color_ctm *ctm)
783{
784    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
785    drmmode_ptr drmmode = drmmode_crtc->drmmode;
786    drmmode_prop_info_ptr ctm_info =
787        &drmmode_crtc->props[DRMMODE_CRTC_CTM];
788    int ret;
789    uint32_t blob_id = 0;
790
791    if (ctm_info->prop_id == 0)
792        return;
793
794    if (ctm && drmmode_crtc->use_gamma_lut && !ctm_is_identity(ctm)) {
795        ret = drmModeCreatePropertyBlob(drmmode->fd, ctm, sizeof(*ctm), &blob_id);
796        if (ret != 0) {
797            xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
798                       "Failed to create CTM property blob: %d\n", ret);
799            blob_id = 0;
800        }
801    }
802
803    ret = drmModeObjectSetProperty(drmmode->fd,
804                                   drmmode_crtc->mode_crtc->crtc_id,
805                                   DRM_MODE_OBJECT_CRTC, ctm_info->prop_id,
806                                   blob_id);
807    if (ret != 0)
808        xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
809                   "Failed to set CTM property: %d\n", ret);
810
811    drmModeDestroyPropertyBlob(drmmode->fd, blob_id);
812}
813
814static int
815drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only)
816{
817    modesettingPtr ms = modesettingPTR(crtc->scrn);
818    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
819    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
820    drmmode_ptr drmmode = drmmode_crtc->drmmode;
821    drmModeModeInfo kmode;
822    int output_count = 0;
823    uint32_t *output_ids = NULL;
824    uint32_t fb_id;
825    int x, y;
826    int i, ret = 0;
827    const struct drm_color_ctm *ctm = NULL;
828
829    if (!drmmode_crtc_get_fb_id(crtc, &fb_id, &x, &y))
830        return 1;
831
832#ifdef GLAMOR_HAS_GBM
833    /* Make sure any pending drawing will be visible in a new scanout buffer */
834    if (drmmode->glamor)
835        glamor_finish(crtc->scrn->pScreen);
836#endif
837
838    if (ms->atomic_modeset) {
839        drmModeAtomicReq *req = drmModeAtomicAlloc();
840        Bool active;
841        uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
842
843        if (!req)
844            return 1;
845
846        ret |= crtc_add_dpms_props(req, crtc, DPMSModeOn, &active);
847        ret |= plane_add_props(req, crtc, active ? fb_id : 0, x, y);
848
849        /* Orphaned CRTCs need to be disabled right now in atomic mode */
850        for (i = 0; i < xf86_config->num_crtc; i++) {
851            xf86CrtcPtr other_crtc = xf86_config->crtc[i];
852            drmmode_crtc_private_ptr other_drmmode_crtc = other_crtc->driver_private;
853            int lost_outputs = 0;
854            int remaining_outputs = 0;
855            int j;
856
857            if (other_crtc == crtc)
858                continue;
859
860            for (j = 0; j < xf86_config->num_output; j++) {
861                xf86OutputPtr output = xf86_config->output[j];
862                drmmode_output_private_ptr drmmode_output = output->driver_private;
863
864                if (drmmode_output->current_crtc == other_crtc) {
865                    if (output->crtc == crtc)
866                        lost_outputs++;
867                    else
868                        remaining_outputs++;
869                }
870            }
871
872            if (lost_outputs > 0 && remaining_outputs == 0) {
873                ret |= crtc_add_prop(req, other_drmmode_crtc,
874                                     DRMMODE_CRTC_ACTIVE, 0);
875                ret |= crtc_add_prop(req, other_drmmode_crtc,
876                                     DRMMODE_CRTC_MODE_ID, 0);
877            }
878        }
879
880        if (test_only)
881            flags |= DRM_MODE_ATOMIC_TEST_ONLY;
882
883        if (ret == 0)
884            ret = drmModeAtomicCommit(ms->fd, req, flags, NULL);
885
886        if (ret == 0 && !test_only) {
887            for (i = 0; i < xf86_config->num_output; i++) {
888                xf86OutputPtr output = xf86_config->output[i];
889                drmmode_output_private_ptr drmmode_output = output->driver_private;
890
891                if (output->crtc == crtc)
892                    drmmode_output->current_crtc = crtc;
893                else if (drmmode_output->current_crtc == crtc)
894                    drmmode_output->current_crtc = NULL;
895            }
896        }
897
898        drmModeAtomicFree(req);
899        return ret;
900    }
901
902    output_ids = calloc(sizeof(uint32_t), xf86_config->num_output);
903    if (!output_ids)
904        return -1;
905
906    for (i = 0; i < xf86_config->num_output; i++) {
907        xf86OutputPtr output = xf86_config->output[i];
908        drmmode_output_private_ptr drmmode_output;
909
910        if (output->crtc != crtc)
911            continue;
912
913        drmmode_output = output->driver_private;
914        if (drmmode_output->output_id == -1)
915            continue;
916        output_ids[output_count] = drmmode_output->output_id;
917        output_count++;
918
919        ctm = &drmmode_output->ctm;
920    }
921
922    drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode);
923    ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
924                         fb_id, x, y, output_ids, output_count, &kmode);
925
926    drmmode_set_ctm(crtc, ctm);
927
928    free(output_ids);
929    return ret;
930}
931
932int
933drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data)
934{
935    modesettingPtr ms = modesettingPTR(crtc->scrn);
936    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
937    int ret;
938
939    if (ms->atomic_modeset) {
940        drmModeAtomicReq *req = drmModeAtomicAlloc();
941
942        if (!req)
943            return 1;
944
945        ret = plane_add_props(req, crtc, fb_id, crtc->x, crtc->y);
946        flags |= DRM_MODE_ATOMIC_NONBLOCK;
947        if (ret == 0)
948            ret = drmModeAtomicCommit(ms->fd, req, flags, data);
949        drmModeAtomicFree(req);
950        return ret;
951    }
952
953    return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id,
954                           fb_id, flags, data);
955}
956
957int
958drmmode_bo_destroy(drmmode_ptr drmmode, drmmode_bo *bo)
959{
960    int ret;
961
962#ifdef GLAMOR_HAS_GBM
963    if (bo->gbm) {
964        gbm_bo_destroy(bo->gbm);
965        bo->gbm = NULL;
966    }
967#endif
968
969    if (bo->dumb) {
970        ret = dumb_bo_destroy(drmmode->fd, bo->dumb);
971        if (ret == 0)
972            bo->dumb = NULL;
973    }
974
975    return 0;
976}
977
978uint32_t
979drmmode_bo_get_pitch(drmmode_bo *bo)
980{
981#ifdef GLAMOR_HAS_GBM
982    if (bo->gbm)
983        return gbm_bo_get_stride(bo->gbm);
984#endif
985
986    return bo->dumb->pitch;
987}
988
989static Bool
990drmmode_bo_has_bo(drmmode_bo *bo)
991{
992#ifdef GLAMOR_HAS_GBM
993    if (bo->gbm)
994        return TRUE;
995#endif
996
997    return bo->dumb != NULL;
998}
999
1000uint32_t
1001drmmode_bo_get_handle(drmmode_bo *bo)
1002{
1003#ifdef GLAMOR_HAS_GBM
1004    if (bo->gbm)
1005        return gbm_bo_get_handle(bo->gbm).u32;
1006#endif
1007
1008    return bo->dumb->handle;
1009}
1010
1011static void *
1012drmmode_bo_map(drmmode_ptr drmmode, drmmode_bo *bo)
1013{
1014    int ret;
1015
1016#ifdef GLAMOR_HAS_GBM
1017    if (bo->gbm)
1018        return NULL;
1019#endif
1020
1021    if (bo->dumb->ptr)
1022        return bo->dumb->ptr;
1023
1024    ret = dumb_bo_map(drmmode->fd, bo->dumb);
1025    if (ret)
1026        return NULL;
1027
1028    return bo->dumb->ptr;
1029}
1030
1031int
1032drmmode_bo_import(drmmode_ptr drmmode, drmmode_bo *bo,
1033                  uint32_t *fb_id)
1034{
1035#ifdef GBM_BO_WITH_MODIFIERS
1036    modesettingPtr ms = modesettingPTR(drmmode->scrn);
1037    if (bo->gbm && ms->kms_has_modifiers &&
1038        gbm_bo_get_modifier(bo->gbm) != DRM_FORMAT_MOD_INVALID) {
1039        int num_fds;
1040
1041        num_fds = gbm_bo_get_plane_count(bo->gbm);
1042        if (num_fds > 0) {
1043            int i;
1044            uint32_t format;
1045            uint32_t handles[4];
1046            uint32_t strides[4];
1047            uint32_t offsets[4];
1048            uint64_t modifiers[4];
1049
1050            memset(handles, 0, sizeof(handles));
1051            memset(strides, 0, sizeof(strides));
1052            memset(offsets, 0, sizeof(offsets));
1053            memset(modifiers, 0, sizeof(modifiers));
1054
1055            format = gbm_bo_get_format(bo->gbm);
1056            format = get_opaque_format(format);
1057            for (i = 0; i < num_fds; i++) {
1058                handles[i] = gbm_bo_get_handle_for_plane(bo->gbm, i).u32;
1059                strides[i] = gbm_bo_get_stride_for_plane(bo->gbm, i);
1060                offsets[i] = gbm_bo_get_offset(bo->gbm, i);
1061                modifiers[i] = gbm_bo_get_modifier(bo->gbm);
1062            }
1063
1064            return drmModeAddFB2WithModifiers(drmmode->fd, bo->width, bo->height,
1065                                              format, handles, strides,
1066                                              offsets, modifiers, fb_id,
1067                                              DRM_MODE_FB_MODIFIERS);
1068        }
1069    }
1070#endif
1071    return drmModeAddFB(drmmode->fd, bo->width, bo->height,
1072                        drmmode->scrn->depth, drmmode->kbpp,
1073                        drmmode_bo_get_pitch(bo),
1074                        drmmode_bo_get_handle(bo), fb_id);
1075}
1076
1077static Bool
1078drmmode_create_bo(drmmode_ptr drmmode, drmmode_bo *bo,
1079                  unsigned width, unsigned height, unsigned bpp)
1080{
1081    bo->width = width;
1082    bo->height = height;
1083
1084#ifdef GLAMOR_HAS_GBM
1085    if (drmmode->glamor) {
1086#ifdef GBM_BO_WITH_MODIFIERS
1087        uint32_t num_modifiers;
1088        uint64_t *modifiers = NULL;
1089#endif
1090        uint32_t format;
1091
1092        switch (drmmode->scrn->depth) {
1093        case 15:
1094            format = GBM_FORMAT_ARGB1555;
1095            break;
1096        case 16:
1097            format = GBM_FORMAT_RGB565;
1098            break;
1099        case 30:
1100            format = GBM_FORMAT_ARGB2101010;
1101            break;
1102        default:
1103            format = GBM_FORMAT_ARGB8888;
1104            break;
1105        }
1106
1107#ifdef GBM_BO_WITH_MODIFIERS
1108        num_modifiers = get_modifiers_set(drmmode->scrn, format, &modifiers,
1109                                          FALSE, TRUE);
1110        if (num_modifiers > 0 &&
1111            !(num_modifiers == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID)) {
1112            bo->gbm = gbm_bo_create_with_modifiers(drmmode->gbm, width, height,
1113                                                   format, modifiers,
1114                                                   num_modifiers);
1115            free(modifiers);
1116            if (bo->gbm) {
1117                bo->used_modifiers = TRUE;
1118                return TRUE;
1119            }
1120        }
1121#endif
1122
1123        bo->gbm = gbm_bo_create(drmmode->gbm, width, height, format,
1124                                GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
1125        bo->used_modifiers = FALSE;
1126        return bo->gbm != NULL;
1127    }
1128#endif
1129
1130    bo->dumb = dumb_bo_create(drmmode->fd, width, height, bpp);
1131    return bo->dumb != NULL;
1132}
1133
1134Bool
1135drmmode_SetSlaveBO(PixmapPtr ppix,
1136                   drmmode_ptr drmmode, int fd_handle, int pitch, int size)
1137{
1138    msPixmapPrivPtr ppriv = msGetPixmapPriv(drmmode, ppix);
1139
1140    if (fd_handle == -1) {
1141        dumb_bo_destroy(drmmode->fd, ppriv->backing_bo);
1142        ppriv->backing_bo = NULL;
1143        return TRUE;
1144    }
1145
1146    ppriv->backing_bo =
1147        dumb_get_bo_from_fd(drmmode->fd, fd_handle, pitch, size);
1148    if (!ppriv->backing_bo)
1149        return FALSE;
1150
1151    close(fd_handle);
1152    return TRUE;
1153}
1154
1155static Bool
1156drmmode_SharedPixmapPresent(PixmapPtr ppix, xf86CrtcPtr crtc,
1157                            drmmode_ptr drmmode)
1158{
1159    ScreenPtr primary = crtc->randr_crtc->pScreen->current_primary;
1160
1161    if (primary->PresentSharedPixmap(ppix)) {
1162        /* Success, queue flip to back target */
1163        if (drmmode_SharedPixmapFlip(ppix, crtc, drmmode))
1164            return TRUE;
1165
1166        xf86DrvMsg(drmmode->scrn->scrnIndex, X_WARNING,
1167                   "drmmode_SharedPixmapFlip() failed, trying again next vblank\n");
1168
1169        return drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, drmmode);
1170    }
1171
1172    /* Failed to present, try again on next vblank after damage */
1173    if (primary->RequestSharedPixmapNotifyDamage) {
1174        msPixmapPrivPtr ppriv = msGetPixmapPriv(drmmode, ppix);
1175
1176        /* Set flag first in case we are immediately notified */
1177        ppriv->wait_for_damage = TRUE;
1178
1179        if (primary->RequestSharedPixmapNotifyDamage(ppix))
1180            return TRUE;
1181        else
1182            ppriv->wait_for_damage = FALSE;
1183    }
1184
1185    /* Damage notification not available, just try again on vblank */
1186    return drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, drmmode);
1187}
1188
1189struct vblank_event_args {
1190    PixmapPtr frontTarget;
1191    PixmapPtr backTarget;
1192    xf86CrtcPtr crtc;
1193    drmmode_ptr drmmode;
1194    Bool flip;
1195};
1196static void
1197drmmode_SharedPixmapVBlankEventHandler(uint64_t frame, uint64_t usec,
1198                                       void *data)
1199{
1200    struct vblank_event_args *args = data;
1201
1202    drmmode_crtc_private_ptr drmmode_crtc = args->crtc->driver_private;
1203
1204    if (args->flip) {
1205        /* frontTarget is being displayed, update crtc to reflect */
1206        drmmode_crtc->prime_pixmap = args->frontTarget;
1207        drmmode_crtc->prime_pixmap_back = args->backTarget;
1208
1209        /* Safe to present on backTarget, no longer displayed */
1210        drmmode_SharedPixmapPresent(args->backTarget, args->crtc, args->drmmode);
1211    } else {
1212        /* backTarget is still being displayed, present on frontTarget */
1213        drmmode_SharedPixmapPresent(args->frontTarget, args->crtc, args->drmmode);
1214    }
1215
1216    free(args);
1217}
1218
1219static void
1220drmmode_SharedPixmapVBlankEventAbort(void *data)
1221{
1222    struct vblank_event_args *args = data;
1223
1224    msGetPixmapPriv(args->drmmode, args->frontTarget)->flip_seq = 0;
1225
1226    free(args);
1227}
1228
1229Bool
1230drmmode_SharedPixmapPresentOnVBlank(PixmapPtr ppix, xf86CrtcPtr crtc,
1231                                    drmmode_ptr drmmode)
1232{
1233    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1234    msPixmapPrivPtr ppriv = msGetPixmapPriv(drmmode, ppix);
1235    struct vblank_event_args *event_args;
1236
1237    if (ppix == drmmode_crtc->prime_pixmap)
1238        return FALSE; /* Already flipped to this pixmap */
1239    if (ppix != drmmode_crtc->prime_pixmap_back)
1240        return FALSE; /* Pixmap is not a scanout pixmap for CRTC */
1241
1242    event_args = calloc(1, sizeof(*event_args));
1243    if (!event_args)
1244        return FALSE;
1245
1246    event_args->frontTarget = ppix;
1247    event_args->backTarget = drmmode_crtc->prime_pixmap;
1248    event_args->crtc = crtc;
1249    event_args->drmmode = drmmode;
1250    event_args->flip = FALSE;
1251
1252    ppriv->flip_seq =
1253        ms_drm_queue_alloc(crtc, event_args,
1254                           drmmode_SharedPixmapVBlankEventHandler,
1255                           drmmode_SharedPixmapVBlankEventAbort);
1256
1257    return ms_queue_vblank(crtc, MS_QUEUE_RELATIVE, 1, NULL, ppriv->flip_seq);
1258}
1259
1260Bool
1261drmmode_SharedPixmapFlip(PixmapPtr frontTarget, xf86CrtcPtr crtc,
1262                         drmmode_ptr drmmode)
1263{
1264    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1265    msPixmapPrivPtr ppriv_front = msGetPixmapPriv(drmmode, frontTarget);
1266
1267    struct vblank_event_args *event_args;
1268
1269    event_args = calloc(1, sizeof(*event_args));
1270    if (!event_args)
1271        return FALSE;
1272
1273    event_args->frontTarget = frontTarget;
1274    event_args->backTarget = drmmode_crtc->prime_pixmap;
1275    event_args->crtc = crtc;
1276    event_args->drmmode = drmmode;
1277    event_args->flip = TRUE;
1278
1279    ppriv_front->flip_seq =
1280        ms_drm_queue_alloc(crtc, event_args,
1281                           drmmode_SharedPixmapVBlankEventHandler,
1282                           drmmode_SharedPixmapVBlankEventAbort);
1283
1284    if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
1285                        ppriv_front->fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1286                        (void *)(intptr_t) ppriv_front->flip_seq) < 0) {
1287        ms_drm_abort_seq(crtc->scrn, ppriv_front->flip_seq);
1288        return FALSE;
1289    }
1290
1291    return TRUE;
1292}
1293
1294static Bool
1295drmmode_InitSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
1296{
1297    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1298
1299    if (!drmmode_crtc->enable_flipping)
1300        return FALSE;
1301
1302    if (drmmode_crtc->flipping_active)
1303        return TRUE;
1304
1305    drmmode_crtc->flipping_active =
1306        drmmode_SharedPixmapPresent(drmmode_crtc->prime_pixmap_back,
1307                                    crtc, drmmode);
1308
1309    return drmmode_crtc->flipping_active;
1310}
1311
1312static void
1313drmmode_FiniSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
1314{
1315    uint32_t seq;
1316    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1317
1318    if (!drmmode_crtc->flipping_active)
1319        return;
1320
1321    drmmode_crtc->flipping_active = FALSE;
1322
1323    /* Abort page flip event handler on prime_pixmap */
1324    seq = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap)->flip_seq;
1325    if (seq)
1326        ms_drm_abort_seq(crtc->scrn, seq);
1327
1328    /* Abort page flip event handler on prime_pixmap_back */
1329    seq = msGetPixmapPriv(drmmode,
1330                          drmmode_crtc->prime_pixmap_back)->flip_seq;
1331    if (seq)
1332        ms_drm_abort_seq(crtc->scrn, seq);
1333}
1334
1335static Bool drmmode_set_target_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix,
1336                                              PixmapPtr *target);
1337
1338Bool
1339drmmode_EnableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode,
1340                                   PixmapPtr front, PixmapPtr back)
1341{
1342    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1343
1344    drmmode_crtc->enable_flipping = TRUE;
1345
1346    /* Set front scanout pixmap */
1347    drmmode_crtc->enable_flipping &=
1348        drmmode_set_target_scanout_pixmap(crtc, front,
1349                                          &drmmode_crtc->prime_pixmap);
1350    if (!drmmode_crtc->enable_flipping)
1351        return FALSE;
1352
1353    /* Set back scanout pixmap */
1354    drmmode_crtc->enable_flipping &=
1355        drmmode_set_target_scanout_pixmap(crtc, back,
1356                                          &drmmode_crtc->prime_pixmap_back);
1357    if (!drmmode_crtc->enable_flipping) {
1358        drmmode_set_target_scanout_pixmap(crtc, NULL,
1359                                          &drmmode_crtc->prime_pixmap);
1360        return FALSE;
1361    }
1362
1363    return TRUE;
1364}
1365
1366void
1367drmmode_DisableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
1368{
1369    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1370
1371    drmmode_crtc->enable_flipping = FALSE;
1372
1373    drmmode_FiniSharedPixmapFlipping(crtc, drmmode);
1374
1375    drmmode_set_target_scanout_pixmap(crtc, NULL, &drmmode_crtc->prime_pixmap);
1376
1377    drmmode_set_target_scanout_pixmap(crtc, NULL,
1378                                      &drmmode_crtc->prime_pixmap_back);
1379}
1380
1381static void
1382drmmode_ConvertFromKMode(ScrnInfoPtr scrn,
1383                         drmModeModeInfo * kmode, DisplayModePtr mode)
1384{
1385    memset(mode, 0, sizeof(DisplayModeRec));
1386    mode->status = MODE_OK;
1387
1388    mode->Clock = kmode->clock;
1389
1390    mode->HDisplay = kmode->hdisplay;
1391    mode->HSyncStart = kmode->hsync_start;
1392    mode->HSyncEnd = kmode->hsync_end;
1393    mode->HTotal = kmode->htotal;
1394    mode->HSkew = kmode->hskew;
1395
1396    mode->VDisplay = kmode->vdisplay;
1397    mode->VSyncStart = kmode->vsync_start;
1398    mode->VSyncEnd = kmode->vsync_end;
1399    mode->VTotal = kmode->vtotal;
1400    mode->VScan = kmode->vscan;
1401
1402    mode->Flags = kmode->flags; //& FLAG_BITS;
1403    mode->name = strdup(kmode->name);
1404
1405    if (kmode->type & DRM_MODE_TYPE_DRIVER)
1406        mode->type = M_T_DRIVER;
1407    if (kmode->type & DRM_MODE_TYPE_PREFERRED)
1408        mode->type |= M_T_PREFERRED;
1409    xf86SetModeCrtc(mode, scrn->adjustFlags);
1410}
1411
1412static void
1413drmmode_ConvertToKMode(ScrnInfoPtr scrn,
1414                       drmModeModeInfo * kmode, DisplayModePtr mode)
1415{
1416    memset(kmode, 0, sizeof(*kmode));
1417
1418    kmode->clock = mode->Clock;
1419    kmode->hdisplay = mode->HDisplay;
1420    kmode->hsync_start = mode->HSyncStart;
1421    kmode->hsync_end = mode->HSyncEnd;
1422    kmode->htotal = mode->HTotal;
1423    kmode->hskew = mode->HSkew;
1424
1425    kmode->vdisplay = mode->VDisplay;
1426    kmode->vsync_start = mode->VSyncStart;
1427    kmode->vsync_end = mode->VSyncEnd;
1428    kmode->vtotal = mode->VTotal;
1429    kmode->vscan = mode->VScan;
1430
1431    kmode->flags = mode->Flags; //& FLAG_BITS;
1432    if (mode->name)
1433        strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN);
1434    kmode->name[DRM_DISPLAY_MODE_LEN - 1] = 0;
1435
1436}
1437
1438static void
1439drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode)
1440{
1441    modesettingPtr ms = modesettingPTR(crtc->scrn);
1442    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1443    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1444
1445    /* XXX Check if DPMS mode is already the right one */
1446
1447    drmmode_crtc->dpms_mode = mode;
1448
1449    if (ms->atomic_modeset) {
1450        if (mode != DPMSModeOn && !ms->pending_modeset)
1451            drmmode_crtc_disable(crtc);
1452    } else if (crtc->enabled == FALSE) {
1453        drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
1454                       0, 0, 0, NULL, 0, NULL);
1455    }
1456}
1457
1458#ifdef GLAMOR_HAS_GBM
1459static PixmapPtr
1460create_pixmap_for_fbcon(drmmode_ptr drmmode, ScrnInfoPtr pScrn, int fbcon_id)
1461{
1462    PixmapPtr pixmap = drmmode->fbcon_pixmap;
1463    drmModeFBPtr fbcon;
1464    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
1465    modesettingPtr ms = modesettingPTR(pScrn);
1466    Bool ret;
1467
1468    if (pixmap)
1469        return pixmap;
1470
1471    fbcon = drmModeGetFB(drmmode->fd, fbcon_id);
1472    if (fbcon == NULL)
1473        return NULL;
1474
1475    if (fbcon->depth != pScrn->depth ||
1476        fbcon->width != pScrn->virtualX ||
1477        fbcon->height != pScrn->virtualY)
1478        goto out_free_fb;
1479
1480    pixmap = drmmode_create_pixmap_header(pScreen, fbcon->width,
1481                                          fbcon->height, fbcon->depth,
1482                                          fbcon->bpp, fbcon->pitch, NULL);
1483    if (!pixmap)
1484        goto out_free_fb;
1485
1486    ret = ms->glamor.egl_create_textured_pixmap(pixmap, fbcon->handle,
1487                                                fbcon->pitch);
1488    if (!ret) {
1489      FreePixmap(pixmap);
1490      pixmap = NULL;
1491    }
1492
1493    drmmode->fbcon_pixmap = pixmap;
1494out_free_fb:
1495    drmModeFreeFB(fbcon);
1496    return pixmap;
1497}
1498#endif
1499
1500void
1501drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
1502{
1503#ifdef GLAMOR_HAS_GBM
1504    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
1505    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
1506    PixmapPtr src, dst;
1507    int fbcon_id = 0;
1508    GCPtr gc;
1509    int i;
1510
1511    for (i = 0; i < xf86_config->num_crtc; i++) {
1512        drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[i]->driver_private;
1513        if (drmmode_crtc->mode_crtc->buffer_id)
1514            fbcon_id = drmmode_crtc->mode_crtc->buffer_id;
1515    }
1516
1517    if (!fbcon_id)
1518        return;
1519
1520    if (fbcon_id == drmmode->fb_id) {
1521        /* in some rare case there might be no fbcon and we might already
1522         * be the one with the current fb to avoid a false deadlck in
1523         * kernel ttm code just do nothing as anyway there is nothing
1524         * to do
1525         */
1526        return;
1527    }
1528
1529    src = create_pixmap_for_fbcon(drmmode, pScrn, fbcon_id);
1530    if (!src)
1531        return;
1532
1533    dst = pScreen->GetScreenPixmap(pScreen);
1534
1535    gc = GetScratchGC(pScrn->depth, pScreen);
1536    ValidateGC(&dst->drawable, gc);
1537
1538    (*gc->ops->CopyArea)(&src->drawable, &dst->drawable, gc, 0, 0,
1539                         pScrn->virtualX, pScrn->virtualY, 0, 0);
1540
1541    FreeScratchGC(gc);
1542
1543    pScreen->canDoBGNoneRoot = TRUE;
1544
1545    if (drmmode->fbcon_pixmap)
1546        pScrn->pScreen->DestroyPixmap(drmmode->fbcon_pixmap);
1547    drmmode->fbcon_pixmap = NULL;
1548#endif
1549}
1550
1551static Bool
1552drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
1553                       Rotation rotation, int x, int y)
1554{
1555    modesettingPtr ms = modesettingPTR(crtc->scrn);
1556    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1557    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1558    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1559    int saved_x, saved_y;
1560    Rotation saved_rotation;
1561    DisplayModeRec saved_mode;
1562    Bool ret = TRUE;
1563    Bool can_test;
1564    int i;
1565
1566    saved_mode = crtc->mode;
1567    saved_x = crtc->x;
1568    saved_y = crtc->y;
1569    saved_rotation = crtc->rotation;
1570
1571    if (mode) {
1572        crtc->mode = *mode;
1573        crtc->x = x;
1574        crtc->y = y;
1575        crtc->rotation = rotation;
1576
1577        if (!xf86CrtcRotate(crtc)) {
1578            goto done;
1579        }
1580
1581        crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
1582                               crtc->gamma_blue, crtc->gamma_size);
1583
1584        can_test = drmmode_crtc_can_test_mode(crtc);
1585        if (drmmode_crtc_set_mode(crtc, can_test)) {
1586            xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
1587                       "failed to set mode: %s\n", strerror(errno));
1588            ret = FALSE;
1589            goto done;
1590        } else
1591            ret = TRUE;
1592
1593        if (crtc->scrn->pScreen)
1594            xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen);
1595
1596        ms->pending_modeset = TRUE;
1597        drmmode_crtc->need_modeset = FALSE;
1598        crtc->funcs->dpms(crtc, DPMSModeOn);
1599
1600        if (drmmode_crtc->prime_pixmap_back)
1601            drmmode_InitSharedPixmapFlipping(crtc, drmmode);
1602
1603        /* go through all the outputs and force DPMS them back on? */
1604        for (i = 0; i < xf86_config->num_output; i++) {
1605            xf86OutputPtr output = xf86_config->output[i];
1606            drmmode_output_private_ptr drmmode_output;
1607
1608            if (output->crtc != crtc)
1609                continue;
1610
1611            drmmode_output = output->driver_private;
1612            if (drmmode_output->output_id == -1)
1613                continue;
1614            output->funcs->dpms(output, DPMSModeOn);
1615        }
1616
1617        /* if we only tested the mode previously, really set it now */
1618        if (can_test)
1619            drmmode_crtc_set_mode(crtc, FALSE);
1620        ms->pending_modeset = FALSE;
1621    }
1622
1623 done:
1624    if (!ret) {
1625        crtc->x = saved_x;
1626        crtc->y = saved_y;
1627        crtc->rotation = saved_rotation;
1628        crtc->mode = saved_mode;
1629    } else
1630        crtc->active = TRUE;
1631
1632    return ret;
1633}
1634
1635static void
1636drmmode_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
1637{
1638
1639}
1640
1641static void
1642drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
1643{
1644    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1645    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1646
1647    drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
1648}
1649
1650static Bool
1651drmmode_set_cursor(xf86CrtcPtr crtc)
1652{
1653    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1654    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1655    uint32_t handle = drmmode_crtc->cursor_bo->handle;
1656    modesettingPtr ms = modesettingPTR(crtc->scrn);
1657    CursorPtr cursor = xf86CurrentCursor(crtc->scrn->pScreen);
1658    int ret = -EINVAL;
1659
1660    if (cursor == NullCursor)
1661	    return TRUE;
1662
1663    ret = drmModeSetCursor2(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
1664                            handle, ms->cursor_width, ms->cursor_height,
1665                            cursor->bits->xhot, cursor->bits->yhot);
1666
1667    /* -EINVAL can mean that an old kernel supports drmModeSetCursor but
1668     * not drmModeSetCursor2, though it can mean other things too. */
1669    if (ret == -EINVAL)
1670        ret = drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
1671                               handle, ms->cursor_width, ms->cursor_height);
1672
1673    /* -ENXIO normally means that the current drm driver supports neither
1674     * cursor_set nor cursor_set2.  Disable hardware cursor support for
1675     * the rest of the session in that case. */
1676    if (ret == -ENXIO) {
1677        xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1678        xf86CursorInfoPtr cursor_info = xf86_config->cursor_info;
1679
1680        cursor_info->MaxWidth = cursor_info->MaxHeight = 0;
1681        drmmode_crtc->drmmode->sw_cursor = TRUE;
1682    }
1683
1684    if (ret)
1685        /* fallback to swcursor */
1686        return FALSE;
1687    return TRUE;
1688}
1689
1690static void drmmode_hide_cursor(xf86CrtcPtr crtc);
1691
1692/*
1693 * The load_cursor_argb_check driver hook.
1694 *
1695 * Sets the hardware cursor by calling the drmModeSetCursor2 ioctl.
1696 * On failure, returns FALSE indicating that the X server should fall
1697 * back to software cursors.
1698 */
1699static Bool
1700drmmode_load_cursor_argb_check(xf86CrtcPtr crtc, CARD32 *image)
1701{
1702    modesettingPtr ms = modesettingPTR(crtc->scrn);
1703    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1704    int i;
1705    uint32_t *ptr;
1706
1707    /* cursor should be mapped already */
1708    ptr = (uint32_t *) (drmmode_crtc->cursor_bo->ptr);
1709
1710    for (i = 0; i < ms->cursor_width * ms->cursor_height; i++)
1711        ptr[i] = image[i];      // cpu_to_le32(image[i]);
1712
1713    if (drmmode_crtc->cursor_up)
1714        return drmmode_set_cursor(crtc);
1715    return TRUE;
1716}
1717
1718static void
1719drmmode_hide_cursor(xf86CrtcPtr crtc)
1720{
1721    modesettingPtr ms = modesettingPTR(crtc->scrn);
1722    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1723    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1724
1725    drmmode_crtc->cursor_up = FALSE;
1726    drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0,
1727                     ms->cursor_width, ms->cursor_height);
1728}
1729
1730static Bool
1731drmmode_show_cursor(xf86CrtcPtr crtc)
1732{
1733    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1734    drmmode_crtc->cursor_up = TRUE;
1735    return drmmode_set_cursor(crtc);
1736}
1737
1738static void
1739drmmode_set_gamma_lut(xf86CrtcPtr crtc,
1740                      uint16_t * red, uint16_t * green, uint16_t * blue,
1741                      int size)
1742{
1743    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1744    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1745    drmmode_prop_info_ptr gamma_lut_info =
1746        &drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT];
1747    const uint32_t crtc_id = drmmode_crtc->mode_crtc->crtc_id;
1748    uint32_t blob_id;
1749    struct drm_color_lut *lut = malloc(sizeof(*lut) * size);
1750
1751    if (lut == NULL) {
1752        xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
1753                   "Failed to allocate memory for %d LUT entries.\n", size);
1754	return;
1755    }
1756
1757    assert(gamma_lut_info->prop_id != 0);
1758
1759    for (int i = 0; i < size; i++) {
1760        lut[i].red = red[i];
1761        lut[i].green = green[i];
1762        lut[i].blue = blue[i];
1763    }
1764
1765    if (!drmModeCreatePropertyBlob(drmmode->fd, lut, sizeof(lut), &blob_id)) {
1766
1767        drmModeObjectSetProperty(drmmode->fd, crtc_id, DRM_MODE_OBJECT_CRTC,
1768                                 gamma_lut_info->prop_id, blob_id);
1769
1770        drmModeDestroyPropertyBlob(drmmode->fd, blob_id);
1771    }
1772    free(lut);
1773}
1774
1775static void
1776drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
1777                       uint16_t * blue, int size)
1778{
1779    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1780    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1781
1782    if (drmmode_crtc->use_gamma_lut) {
1783        drmmode_set_gamma_lut(crtc, red, green, blue, size);
1784    } else {
1785        drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
1786                            size, red, green, blue);
1787    }
1788}
1789
1790static Bool
1791drmmode_set_target_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix,
1792                                      PixmapPtr *target)
1793{
1794    ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
1795    PixmapPtr screenpix = screen->GetScreenPixmap(screen);
1796    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
1797    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1798    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1799    int c, total_width = 0, max_height = 0, this_x = 0;
1800
1801    if (*target) {
1802        PixmapStopDirtyTracking(&(*target)->drawable, screenpix);
1803        if (drmmode->fb_id) {
1804            drmModeRmFB(drmmode->fd, drmmode->fb_id);
1805            drmmode->fb_id = 0;
1806        }
1807        drmmode_crtc->prime_pixmap_x = 0;
1808        *target = NULL;
1809    }
1810
1811    if (!ppix)
1812        return TRUE;
1813
1814    /* iterate over all the attached crtcs to work out the bounding box */
1815    for (c = 0; c < xf86_config->num_crtc; c++) {
1816        xf86CrtcPtr iter = xf86_config->crtc[c];
1817        if (!iter->enabled && iter != crtc)
1818            continue;
1819        if (iter == crtc) {
1820            this_x = total_width;
1821            total_width += ppix->drawable.width;
1822            if (max_height < ppix->drawable.height)
1823                max_height = ppix->drawable.height;
1824        } else {
1825            total_width += iter->mode.HDisplay;
1826            if (max_height < iter->mode.VDisplay)
1827                max_height = iter->mode.VDisplay;
1828        }
1829    }
1830
1831    if (total_width != screenpix->drawable.width ||
1832        max_height != screenpix->drawable.height) {
1833
1834        if (!drmmode_xf86crtc_resize(crtc->scrn, total_width, max_height))
1835            return FALSE;
1836
1837        screenpix = screen->GetScreenPixmap(screen);
1838        screen->width = screenpix->drawable.width = total_width;
1839        screen->height = screenpix->drawable.height = max_height;
1840    }
1841    drmmode_crtc->prime_pixmap_x = this_x;
1842    PixmapStartDirtyTracking(&ppix->drawable, screenpix, 0, 0, this_x, 0,
1843                             RR_Rotate_0);
1844    *target = ppix;
1845    return TRUE;
1846}
1847
1848static Bool
1849drmmode_set_target_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix,
1850                                      PixmapPtr *target)
1851{
1852    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1853    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1854    msPixmapPrivPtr ppriv;
1855    void *ptr;
1856
1857    if (*target) {
1858        ppriv = msGetPixmapPriv(drmmode, *target);
1859        drmModeRmFB(drmmode->fd, ppriv->fb_id);
1860        ppriv->fb_id = 0;
1861        if (ppriv->secondary_damage) {
1862            DamageUnregister(ppriv->secondary_damage);
1863            ppriv->secondary_damage = NULL;
1864        }
1865        *target = NULL;
1866    }
1867
1868    if (!ppix)
1869        return TRUE;
1870
1871    ppriv = msGetPixmapPriv(drmmode, ppix);
1872    if (!ppriv->secondary_damage) {
1873        ppriv->secondary_damage = DamageCreate(NULL, NULL,
1874                                           DamageReportNone,
1875                                           TRUE,
1876                                           crtc->randr_crtc->pScreen,
1877                                           NULL);
1878    }
1879    ptr = drmmode_map_secondary_bo(drmmode, ppriv);
1880    ppix->devPrivate.ptr = ptr;
1881    DamageRegister(&ppix->drawable, ppriv->secondary_damage);
1882
1883    if (ppriv->fb_id == 0) {
1884        int ret = drmModeAddFB(drmmode->fd, ppix->drawable.width,
1885                     ppix->drawable.height,
1886                     ppix->drawable.depth,
1887                     ppix->drawable.bitsPerPixel,
1888                     ppix->devKind, ppriv->backing_bo->handle, &ppriv->fb_id);
1889	if (ret) {
1890	    ErrorF("failed to set scanout pixmap cpu\n");
1891	    return FALSE;
1892	}
1893    }
1894    *target = ppix;
1895    return TRUE;
1896}
1897
1898static Bool
1899drmmode_set_target_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix,
1900                                  PixmapPtr *target)
1901{
1902    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1903    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1904
1905    if (drmmode->reverse_prime_offload_mode)
1906        return drmmode_set_target_scanout_pixmap_gpu(crtc, ppix, target);
1907    else
1908        return drmmode_set_target_scanout_pixmap_cpu(crtc, ppix, target);
1909}
1910
1911static Bool
1912drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
1913{
1914    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1915
1916    /* Use DisableSharedPixmapFlipping before switching to single buf */
1917    if (drmmode_crtc->enable_flipping)
1918        return FALSE;
1919
1920    return drmmode_set_target_scanout_pixmap(crtc, ppix,
1921                                             &drmmode_crtc->prime_pixmap);
1922}
1923
1924static void
1925drmmode_clear_pixmap(PixmapPtr pixmap)
1926{
1927    ScreenPtr screen = pixmap->drawable.pScreen;
1928    GCPtr gc;
1929#ifdef GLAMOR_HAS_GBM
1930    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
1931
1932    if (ms->drmmode.glamor) {
1933        ms->glamor.clear_pixmap(pixmap);
1934        return;
1935    }
1936#endif
1937
1938    gc = GetScratchGC(pixmap->drawable.depth, screen);
1939    if (gc) {
1940        miClearDrawable(&pixmap->drawable, gc);
1941        FreeScratchGC(gc);
1942    }
1943}
1944
1945static void *
1946drmmode_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
1947{
1948    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
1949    drmmode_ptr drmmode = drmmode_crtc->drmmode;
1950    int ret;
1951
1952    if (!drmmode_create_bo(drmmode, &drmmode_crtc->rotate_bo,
1953                           width, height, drmmode->kbpp)) {
1954        xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR,
1955               "Couldn't allocate shadow memory for rotated CRTC\n");
1956        return NULL;
1957    }
1958
1959    ret = drmmode_bo_import(drmmode, &drmmode_crtc->rotate_bo,
1960                            &drmmode_crtc->rotate_fb_id);
1961
1962    if (ret) {
1963        ErrorF("failed to add rotate fb\n");
1964        drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
1965        return NULL;
1966    }
1967
1968#ifdef GLAMOR_HAS_GBM
1969    if (drmmode->gbm)
1970        return drmmode_crtc->rotate_bo.gbm;
1971#endif
1972    return drmmode_crtc->rotate_bo.dumb;
1973}
1974
1975static PixmapPtr
1976drmmode_create_pixmap_header(ScreenPtr pScreen, int width, int height,
1977                             int depth, int bitsPerPixel, int devKind,
1978                             void *pPixData)
1979{
1980    PixmapPtr pixmap;
1981
1982    /* width and height of 0 means don't allocate any pixmap data */
1983    pixmap = (*pScreen->CreatePixmap)(pScreen, 0, 0, depth, 0);
1984
1985    if (pixmap) {
1986        if ((*pScreen->ModifyPixmapHeader)(pixmap, width, height, depth,
1987                                           bitsPerPixel, devKind, pPixData))
1988            return pixmap;
1989        (*pScreen->DestroyPixmap)(pixmap);
1990    }
1991    return NullPixmap;
1992}
1993
1994static Bool
1995drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo);
1996
1997static PixmapPtr
1998drmmode_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
1999{
2000    ScrnInfoPtr scrn = crtc->scrn;
2001    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2002    drmmode_ptr drmmode = drmmode_crtc->drmmode;
2003    uint32_t rotate_pitch;
2004    PixmapPtr rotate_pixmap;
2005    void *pPixData = NULL;
2006
2007    if (!data) {
2008        data = drmmode_shadow_allocate(crtc, width, height);
2009        if (!data) {
2010            xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2011                       "Couldn't allocate shadow pixmap for rotated CRTC\n");
2012            return NULL;
2013        }
2014    }
2015
2016    if (!drmmode_bo_has_bo(&drmmode_crtc->rotate_bo)) {
2017        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2018                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
2019        return NULL;
2020    }
2021
2022    pPixData = drmmode_bo_map(drmmode, &drmmode_crtc->rotate_bo);
2023    rotate_pitch = drmmode_bo_get_pitch(&drmmode_crtc->rotate_bo);
2024
2025    rotate_pixmap = drmmode_create_pixmap_header(scrn->pScreen,
2026                                                 width, height,
2027                                                 scrn->depth,
2028                                                 drmmode->kbpp,
2029                                                 rotate_pitch,
2030                                                 pPixData);
2031
2032    if (rotate_pixmap == NULL) {
2033        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
2034                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
2035        return NULL;
2036    }
2037
2038    drmmode_set_pixmap_bo(drmmode, rotate_pixmap, &drmmode_crtc->rotate_bo);
2039
2040    return rotate_pixmap;
2041}
2042
2043static void
2044drmmode_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data)
2045{
2046    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2047    drmmode_ptr drmmode = drmmode_crtc->drmmode;
2048
2049    if (rotate_pixmap) {
2050        rotate_pixmap->drawable.pScreen->DestroyPixmap(rotate_pixmap);
2051    }
2052
2053    if (data) {
2054        drmModeRmFB(drmmode->fd, drmmode_crtc->rotate_fb_id);
2055        drmmode_crtc->rotate_fb_id = 0;
2056
2057        drmmode_bo_destroy(drmmode, &drmmode_crtc->rotate_bo);
2058        memset(&drmmode_crtc->rotate_bo, 0, sizeof drmmode_crtc->rotate_bo);
2059    }
2060}
2061
2062static void
2063drmmode_crtc_destroy(xf86CrtcPtr crtc)
2064{
2065    drmmode_mode_ptr iterator, next;
2066    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2067    modesettingPtr ms = modesettingPTR(crtc->scrn);
2068
2069    if (!ms->atomic_modeset)
2070        return;
2071
2072    drmmode_prop_info_free(drmmode_crtc->props_plane, DRMMODE_PLANE__COUNT);
2073    xorg_list_for_each_entry_safe(iterator, next, &drmmode_crtc->mode_list, entry) {
2074        drm_mode_destroy(crtc, iterator);
2075    }
2076}
2077
2078static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
2079    .dpms = drmmode_crtc_dpms,
2080    .set_mode_major = drmmode_set_mode_major,
2081    .set_cursor_colors = drmmode_set_cursor_colors,
2082    .set_cursor_position = drmmode_set_cursor_position,
2083    .show_cursor_check = drmmode_show_cursor,
2084    .hide_cursor = drmmode_hide_cursor,
2085    .load_cursor_argb_check = drmmode_load_cursor_argb_check,
2086
2087    .gamma_set = drmmode_crtc_gamma_set,
2088    .destroy = drmmode_crtc_destroy,
2089    .set_scanout_pixmap = drmmode_set_scanout_pixmap,
2090    .shadow_allocate = drmmode_shadow_allocate,
2091    .shadow_create = drmmode_shadow_create,
2092    .shadow_destroy = drmmode_shadow_destroy,
2093};
2094
2095static uint32_t
2096drmmode_crtc_vblank_pipe(int crtc_id)
2097{
2098    if (crtc_id > 1)
2099        return crtc_id << DRM_VBLANK_HIGH_CRTC_SHIFT;
2100    else if (crtc_id > 0)
2101        return DRM_VBLANK_SECONDARY;
2102    else
2103        return 0;
2104}
2105
2106static Bool
2107is_plane_assigned(ScrnInfoPtr scrn, int plane_id)
2108{
2109    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
2110    int c;
2111
2112    for (c = 0; c < xf86_config->num_crtc; c++) {
2113        xf86CrtcPtr iter = xf86_config->crtc[c];
2114        drmmode_crtc_private_ptr drmmode_crtc = iter->driver_private;
2115        if (drmmode_crtc->plane_id == plane_id)
2116            return TRUE;
2117    }
2118
2119    return FALSE;
2120}
2121
2122/**
2123 * Populates the formats array, and the modifiers of each format for a drm_plane.
2124 */
2125static Bool
2126populate_format_modifiers(xf86CrtcPtr crtc, const drmModePlane *kplane,
2127                          uint32_t blob_id)
2128{
2129    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2130    drmmode_ptr drmmode = drmmode_crtc->drmmode;
2131    unsigned i, j;
2132    drmModePropertyBlobRes *blob;
2133    struct drm_format_modifier_blob *fmt_mod_blob;
2134    uint32_t *blob_formats;
2135    struct drm_format_modifier *blob_modifiers;
2136
2137    if (!blob_id)
2138        return FALSE;
2139
2140    blob = drmModeGetPropertyBlob(drmmode->fd, blob_id);
2141    if (!blob)
2142        return FALSE;
2143
2144    fmt_mod_blob = blob->data;
2145    blob_formats = formats_ptr(fmt_mod_blob);
2146    blob_modifiers = modifiers_ptr(fmt_mod_blob);
2147
2148    assert(drmmode_crtc->num_formats == fmt_mod_blob->count_formats);
2149
2150    for (i = 0; i < fmt_mod_blob->count_formats; i++) {
2151        uint32_t num_modifiers = 0;
2152        uint64_t *modifiers = NULL;
2153        uint64_t *tmp;
2154        for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
2155            struct drm_format_modifier *mod = &blob_modifiers[j];
2156
2157            if ((i < mod->offset) || (i > mod->offset + 63))
2158                continue;
2159            if (!(mod->formats & (1 << (i - mod->offset))))
2160                continue;
2161
2162            num_modifiers++;
2163            tmp = realloc(modifiers, num_modifiers * sizeof(modifiers[0]));
2164            if (!tmp) {
2165                free(modifiers);
2166                drmModeFreePropertyBlob(blob);
2167                return FALSE;
2168            }
2169            modifiers = tmp;
2170            modifiers[num_modifiers - 1] = mod->modifier;
2171        }
2172
2173        drmmode_crtc->formats[i].format = blob_formats[i];
2174        drmmode_crtc->formats[i].modifiers = modifiers;
2175        drmmode_crtc->formats[i].num_modifiers = num_modifiers;
2176    }
2177
2178    drmModeFreePropertyBlob(blob);
2179
2180    return TRUE;
2181}
2182
2183static void
2184drmmode_crtc_create_planes(xf86CrtcPtr crtc, int num)
2185{
2186    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2187    drmmode_ptr drmmode = drmmode_crtc->drmmode;
2188    drmModePlaneRes *kplane_res;
2189    drmModePlane *kplane, *best_kplane = NULL;
2190    drmModeObjectProperties *props;
2191    uint32_t i, type, blob_id;
2192    int current_crtc, best_plane = 0;
2193
2194    static drmmode_prop_enum_info_rec plane_type_enums[] = {
2195        [DRMMODE_PLANE_TYPE_PRIMARY] = {
2196            .name = "Primary",
2197        },
2198        [DRMMODE_PLANE_TYPE_OVERLAY] = {
2199            .name = "Overlay",
2200        },
2201        [DRMMODE_PLANE_TYPE_CURSOR] = {
2202            .name = "Cursor",
2203        },
2204    };
2205    static const drmmode_prop_info_rec plane_props[] = {
2206        [DRMMODE_PLANE_TYPE] = {
2207            .name = "type",
2208            .enum_values = plane_type_enums,
2209            .num_enum_values = DRMMODE_PLANE_TYPE__COUNT,
2210        },
2211        [DRMMODE_PLANE_FB_ID] = { .name = "FB_ID", },
2212        [DRMMODE_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
2213        [DRMMODE_PLANE_IN_FORMATS] = { .name = "IN_FORMATS", },
2214        [DRMMODE_PLANE_SRC_X] = { .name = "SRC_X", },
2215        [DRMMODE_PLANE_SRC_Y] = { .name = "SRC_Y", },
2216        [DRMMODE_PLANE_SRC_W] = { .name = "SRC_W", },
2217        [DRMMODE_PLANE_SRC_H] = { .name = "SRC_H", },
2218        [DRMMODE_PLANE_CRTC_X] = { .name = "CRTC_X", },
2219        [DRMMODE_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
2220        [DRMMODE_PLANE_CRTC_W] = { .name = "CRTC_W", },
2221        [DRMMODE_PLANE_CRTC_H] = { .name = "CRTC_H", },
2222    };
2223    drmmode_prop_info_rec tmp_props[DRMMODE_PLANE__COUNT];
2224
2225    if (!drmmode_prop_info_copy(tmp_props, plane_props, DRMMODE_PLANE__COUNT, 0)) {
2226        xf86DrvMsg(drmmode->scrn->scrnIndex, X_ERROR,
2227                   "failed to copy plane property info\n");
2228        drmmode_prop_info_free(tmp_props, DRMMODE_PLANE__COUNT);
2229        return;
2230    }
2231
2232    kplane_res = drmModeGetPlaneResources(drmmode->fd);
2233    if (!kplane_res) {
2234        xf86DrvMsg(drmmode->scrn->scrnIndex, X_ERROR,
2235                   "failed to get plane resources: %s\n", strerror(errno));
2236        drmmode_prop_info_free(tmp_props, DRMMODE_PLANE__COUNT);
2237        return;
2238    }
2239
2240    for (i = 0; i < kplane_res->count_planes; i++) {
2241        int plane_id;
2242
2243        kplane = drmModeGetPlane(drmmode->fd, kplane_res->planes[i]);
2244        if (!kplane)
2245            continue;
2246
2247        if (!(kplane->possible_crtcs & (1 << num)) ||
2248            is_plane_assigned(drmmode->scrn, kplane->plane_id)) {
2249            drmModeFreePlane(kplane);
2250            continue;
2251        }
2252
2253        plane_id = kplane->plane_id;
2254
2255        props = drmModeObjectGetProperties(drmmode->fd, plane_id,
2256                                           DRM_MODE_OBJECT_PLANE);
2257        if (!props) {
2258            xf86DrvMsg(drmmode->scrn->scrnIndex, X_ERROR,
2259                    "couldn't get plane properties\n");
2260            drmModeFreePlane(kplane);
2261            continue;
2262        }
2263
2264        drmmode_prop_info_update(drmmode, tmp_props, DRMMODE_PLANE__COUNT, props);
2265
2266        /* Only primary planes are important for atomic page-flipping */
2267        type = drmmode_prop_get_value(&tmp_props[DRMMODE_PLANE_TYPE],
2268                                      props, DRMMODE_PLANE_TYPE__COUNT);
2269        if (type != DRMMODE_PLANE_TYPE_PRIMARY) {
2270            drmModeFreePlane(kplane);
2271            drmModeFreeObjectProperties(props);
2272            continue;
2273        }
2274
2275        /* Check if plane is already on this CRTC */
2276        current_crtc = drmmode_prop_get_value(&tmp_props[DRMMODE_PLANE_CRTC_ID],
2277                                              props, 0);
2278        if (current_crtc == drmmode_crtc->mode_crtc->crtc_id) {
2279            if (best_plane) {
2280                drmModeFreePlane(best_kplane);
2281                drmmode_prop_info_free(drmmode_crtc->props_plane, DRMMODE_PLANE__COUNT);
2282            }
2283            best_plane = plane_id;
2284            best_kplane = kplane;
2285            blob_id = drmmode_prop_get_value(&tmp_props[DRMMODE_PLANE_IN_FORMATS],
2286                                             props, 0);
2287            drmmode_prop_info_copy(drmmode_crtc->props_plane, tmp_props,
2288                                   DRMMODE_PLANE__COUNT, 1);
2289            drmModeFreeObjectProperties(props);
2290            break;
2291        }
2292
2293        if (!best_plane) {
2294            best_plane = plane_id;
2295            best_kplane = kplane;
2296            blob_id = drmmode_prop_get_value(&tmp_props[DRMMODE_PLANE_IN_FORMATS],
2297                                             props, 0);
2298            drmmode_prop_info_copy(drmmode_crtc->props_plane, tmp_props,
2299                                   DRMMODE_PLANE__COUNT, 1);
2300        } else {
2301            drmModeFreePlane(kplane);
2302        }
2303
2304        drmModeFreeObjectProperties(props);
2305    }
2306
2307    drmmode_crtc->plane_id = best_plane;
2308    if (best_kplane) {
2309        drmmode_crtc->num_formats = best_kplane->count_formats;
2310        drmmode_crtc->formats = calloc(sizeof(drmmode_format_rec),
2311                                       best_kplane->count_formats);
2312        if (!populate_format_modifiers(crtc, best_kplane, blob_id)) {
2313            for (i = 0; i < best_kplane->count_formats; i++)
2314                drmmode_crtc->formats[i].format = best_kplane->formats[i];
2315        }
2316        drmModeFreePlane(best_kplane);
2317    }
2318
2319    drmmode_prop_info_free(tmp_props, DRMMODE_PLANE__COUNT);
2320    drmModeFreePlaneResources(kplane_res);
2321}
2322
2323static uint32_t
2324drmmode_crtc_get_prop_id(uint32_t drm_fd,
2325                         drmModeObjectPropertiesPtr props,
2326                         char const* name)
2327{
2328    uint32_t i, prop_id = 0;
2329
2330    for (i = 0; !prop_id && i < props->count_props; ++i) {
2331        drmModePropertyPtr drm_prop =
2332                     drmModeGetProperty(drm_fd, props->props[i]);
2333
2334        if (!drm_prop)
2335            continue;
2336
2337        if (strcmp(drm_prop->name, name) == 0)
2338            prop_id = drm_prop->prop_id;
2339
2340        drmModeFreeProperty(drm_prop);
2341    }
2342
2343    return prop_id;
2344}
2345
2346static void
2347drmmode_crtc_vrr_init(int drm_fd, xf86CrtcPtr crtc)
2348{
2349    drmModeObjectPropertiesPtr drm_props;
2350    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2351    drmmode_ptr drmmode = drmmode_crtc->drmmode;
2352
2353    if (drmmode->vrr_prop_id)
2354        return;
2355
2356    drm_props = drmModeObjectGetProperties(drm_fd,
2357                                           drmmode_crtc->mode_crtc->crtc_id,
2358                                           DRM_MODE_OBJECT_CRTC);
2359
2360    if (!drm_props)
2361        return;
2362
2363    drmmode->vrr_prop_id = drmmode_crtc_get_prop_id(drm_fd,
2364                                                    drm_props,
2365                                                    "VRR_ENABLED");
2366
2367    drmModeFreeObjectProperties(drm_props);
2368}
2369
2370static unsigned int
2371drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
2372{
2373    xf86CrtcPtr crtc;
2374    drmmode_crtc_private_ptr drmmode_crtc;
2375    modesettingEntPtr ms_ent = ms_ent_priv(pScrn);
2376    drmModeObjectPropertiesPtr props;
2377    static const drmmode_prop_info_rec crtc_props[] = {
2378        [DRMMODE_CRTC_ACTIVE] = { .name = "ACTIVE" },
2379        [DRMMODE_CRTC_MODE_ID] = { .name = "MODE_ID" },
2380        [DRMMODE_CRTC_GAMMA_LUT] = { .name = "GAMMA_LUT" },
2381        [DRMMODE_CRTC_GAMMA_LUT_SIZE] = { .name = "GAMMA_LUT_SIZE" },
2382        [DRMMODE_CRTC_CTM] = { .name = "CTM" },
2383    };
2384
2385    crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs);
2386    if (crtc == NULL)
2387        return 0;
2388    drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
2389    crtc->driver_private = drmmode_crtc;
2390    drmmode_crtc->mode_crtc =
2391        drmModeGetCrtc(drmmode->fd, mode_res->crtcs[num]);
2392    drmmode_crtc->drmmode = drmmode;
2393    drmmode_crtc->vblank_pipe = drmmode_crtc_vblank_pipe(num);
2394    xorg_list_init(&drmmode_crtc->mode_list);
2395
2396    props = drmModeObjectGetProperties(drmmode->fd, mode_res->crtcs[num],
2397                                       DRM_MODE_OBJECT_CRTC);
2398    if (!props || !drmmode_prop_info_copy(drmmode_crtc->props, crtc_props,
2399                                          DRMMODE_CRTC__COUNT, 0)) {
2400        xf86CrtcDestroy(crtc);
2401        return 0;
2402    }
2403
2404    drmmode_prop_info_update(drmmode, drmmode_crtc->props,
2405                             DRMMODE_CRTC__COUNT, props);
2406    drmModeFreeObjectProperties(props);
2407    drmmode_crtc_create_planes(crtc, num);
2408
2409    /* Hide any cursors which may be active from previous users */
2410    drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, 0, 0);
2411
2412    drmmode_crtc_vrr_init(drmmode->fd, crtc);
2413
2414    /* Mark num'th crtc as in use on this device. */
2415    ms_ent->assigned_crtcs |= (1 << num);
2416    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, MS_LOGLEVEL_DEBUG,
2417                   "Allocated crtc nr. %d to this screen.\n", num);
2418
2419    if (drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT_SIZE].prop_id &&
2420        drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT_SIZE].value) {
2421        /*
2422         * GAMMA_LUT property supported, and so far tested to be safe to use by
2423         * default for lut sizes up to 4096 slots. Intel Tigerlake+ has some
2424         * issues, and a large GAMMA_LUT with 262145 slots, so keep GAMMA_LUT
2425         * off for large lut sizes by default for now.
2426         */
2427        drmmode_crtc->use_gamma_lut = drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT_SIZE].value <= 4096;
2428
2429        /* Allow config override. */
2430        drmmode_crtc->use_gamma_lut = xf86ReturnOptValBool(drmmode->Options,
2431                                                           OPTION_USE_GAMMA_LUT,
2432                                                           drmmode_crtc->use_gamma_lut);
2433    } else {
2434        drmmode_crtc->use_gamma_lut = FALSE;
2435    }
2436
2437    if (drmmode_crtc->use_gamma_lut &&
2438        drmmode_crtc->props[DRMMODE_CRTC_CTM].prop_id) {
2439        drmmode->use_ctm = TRUE;
2440    }
2441
2442    return 1;
2443}
2444
2445/*
2446 * Update all of the property values for an output
2447 */
2448static void
2449drmmode_output_update_properties(xf86OutputPtr output)
2450{
2451    drmmode_output_private_ptr drmmode_output = output->driver_private;
2452    int i, j, k;
2453    int err;
2454    drmModeConnectorPtr koutput;
2455
2456    /* Use the most recently fetched values from the kernel */
2457    koutput = drmmode_output->mode_output;
2458
2459    if (!koutput)
2460        return;
2461
2462    for (i = 0; i < drmmode_output->num_props; i++) {
2463        drmmode_prop_ptr p = &drmmode_output->props[i];
2464
2465        for (j = 0; koutput && j < koutput->count_props; j++) {
2466            if (koutput->props[j] == p->mode_prop->prop_id) {
2467
2468                /* Check to see if the property value has changed */
2469                if (koutput->prop_values[j] != p->value) {
2470
2471                    p->value = koutput->prop_values[j];
2472
2473                    if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
2474                        INT32 value = p->value;
2475
2476                        err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
2477                                                     XA_INTEGER, 32, PropModeReplace, 1,
2478                                                     &value, FALSE, TRUE);
2479
2480                        if (err != 0) {
2481                            xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2482                                       "RRChangeOutputProperty error, %d\n", err);
2483                        }
2484                    }
2485                    else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
2486                        for (k = 0; k < p->mode_prop->count_enums; k++)
2487                            if (p->mode_prop->enums[k].value == p->value)
2488                                break;
2489                        if (k < p->mode_prop->count_enums) {
2490                            err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
2491                                                         XA_ATOM, 32, PropModeReplace, 1,
2492                                                         &p->atoms[k + 1], FALSE, TRUE);
2493                            if (err != 0) {
2494                                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2495                                           "RRChangeOutputProperty error, %d\n", err);
2496                            }
2497                        }
2498                    }
2499                }
2500                break;
2501            }
2502        }
2503    }
2504
2505    /* Update the CTM property */
2506    if (drmmode_output->ctm_atom) {
2507        err = RRChangeOutputProperty(output->randr_output,
2508                                     drmmode_output->ctm_atom,
2509                                     XA_INTEGER, 32, PropModeReplace, 18,
2510                                     &drmmode_output->ctm,
2511                                     FALSE, TRUE);
2512        if (err != 0) {
2513            xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2514                       "RRChangeOutputProperty error, %d\n", err);
2515        }
2516    }
2517
2518}
2519
2520static xf86OutputStatus
2521drmmode_output_detect(xf86OutputPtr output)
2522{
2523    /* go to the hw and retrieve a new output struct */
2524    drmmode_output_private_ptr drmmode_output = output->driver_private;
2525    drmmode_ptr drmmode = drmmode_output->drmmode;
2526    xf86OutputStatus status;
2527
2528    if (drmmode_output->output_id == -1)
2529        return XF86OutputStatusDisconnected;
2530
2531    drmModeFreeConnector(drmmode_output->mode_output);
2532
2533    drmmode_output->mode_output =
2534        drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
2535
2536    if (!drmmode_output->mode_output) {
2537        drmmode_output->output_id = -1;
2538        return XF86OutputStatusDisconnected;
2539    }
2540
2541    drmmode_output_update_properties(output);
2542
2543    switch (drmmode_output->mode_output->connection) {
2544    case DRM_MODE_CONNECTED:
2545        status = XF86OutputStatusConnected;
2546        break;
2547    case DRM_MODE_DISCONNECTED:
2548        status = XF86OutputStatusDisconnected;
2549        break;
2550    default:
2551    case DRM_MODE_UNKNOWNCONNECTION:
2552        status = XF86OutputStatusUnknown;
2553        break;
2554    }
2555    return status;
2556}
2557
2558static Bool
2559drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes)
2560{
2561    return MODE_OK;
2562}
2563
2564static int
2565koutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
2566        int type, const char *name)
2567{
2568    int idx = -1;
2569
2570    for (int i = 0; i < koutput->count_props; i++) {
2571        drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
2572
2573        if (!prop)
2574            continue;
2575
2576        if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
2577            idx = i;
2578
2579        drmModeFreeProperty(prop);
2580
2581        if (idx > -1)
2582            break;
2583    }
2584
2585    return idx;
2586}
2587
2588static int
2589koutput_get_prop_id(int fd, drmModeConnectorPtr koutput,
2590        int type, const char *name)
2591{
2592    int idx = koutput_get_prop_idx(fd, koutput, type, name);
2593
2594    return (idx > -1) ? koutput->props[idx] : -1;
2595}
2596
2597static drmModePropertyBlobPtr
2598koutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
2599{
2600    drmModePropertyBlobPtr blob = NULL;
2601    int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
2602
2603    if (idx > -1)
2604        blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
2605
2606    return blob;
2607}
2608
2609static void
2610drmmode_output_attach_tile(xf86OutputPtr output)
2611{
2612    drmmode_output_private_ptr drmmode_output = output->driver_private;
2613    drmModeConnectorPtr koutput = drmmode_output->mode_output;
2614    drmmode_ptr drmmode = drmmode_output->drmmode;
2615    struct xf86CrtcTileInfo tile_info, *set = NULL;
2616
2617    if (!koutput) {
2618        xf86OutputSetTile(output, NULL);
2619        return;
2620    }
2621
2622    drmModeFreePropertyBlob(drmmode_output->tile_blob);
2623
2624    /* look for a TILE property */
2625    drmmode_output->tile_blob =
2626        koutput_get_prop_blob(drmmode->fd, koutput, "TILE");
2627
2628    if (drmmode_output->tile_blob) {
2629        if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data, drmmode_output->tile_blob->length, &tile_info) == TRUE)
2630            set = &tile_info;
2631    }
2632    xf86OutputSetTile(output, set);
2633}
2634
2635static Bool
2636has_panel_fitter(xf86OutputPtr output)
2637{
2638    drmmode_output_private_ptr drmmode_output = output->driver_private;
2639    drmModeConnectorPtr koutput = drmmode_output->mode_output;
2640    drmmode_ptr drmmode = drmmode_output->drmmode;
2641    int idx;
2642
2643    /* Presume that if the output supports scaling, then we have a
2644     * panel fitter capable of adjust any mode to suit.
2645     */
2646    idx = koutput_get_prop_idx(drmmode->fd, koutput,
2647            DRM_MODE_PROP_ENUM, "scaling mode");
2648
2649    return (idx > -1);
2650}
2651
2652static DisplayModePtr
2653drmmode_output_add_gtf_modes(xf86OutputPtr output, DisplayModePtr Modes)
2654{
2655    xf86MonPtr mon = output->MonInfo;
2656    DisplayModePtr i, m, preferred = NULL;
2657    int max_x = 0, max_y = 0;
2658    float max_vrefresh = 0.0;
2659
2660    if (mon && gtf_supported(mon))
2661        return Modes;
2662
2663    if (!has_panel_fitter(output))
2664        return Modes;
2665
2666    for (m = Modes; m; m = m->next) {
2667        if (m->type & M_T_PREFERRED)
2668            preferred = m;
2669        max_x = max(max_x, m->HDisplay);
2670        max_y = max(max_y, m->VDisplay);
2671        max_vrefresh = max(max_vrefresh, xf86ModeVRefresh(m));
2672    }
2673
2674    max_vrefresh = max(max_vrefresh, 60.0);
2675    max_vrefresh *= (1 + SYNC_TOLERANCE);
2676
2677    m = xf86GetDefaultModes();
2678    xf86ValidateModesSize(output->scrn, m, max_x, max_y, 0);
2679
2680    for (i = m; i; i = i->next) {
2681        if (xf86ModeVRefresh(i) > max_vrefresh)
2682            i->status = MODE_VSYNC;
2683        if (preferred &&
2684            i->HDisplay >= preferred->HDisplay &&
2685            i->VDisplay >= preferred->VDisplay &&
2686            xf86ModeVRefresh(i) >= xf86ModeVRefresh(preferred))
2687            i->status = MODE_VSYNC;
2688    }
2689
2690    xf86PruneInvalidModes(output->scrn, &m, FALSE);
2691
2692    return xf86ModesAdd(Modes, m);
2693}
2694
2695static DisplayModePtr
2696drmmode_output_get_modes(xf86OutputPtr output)
2697{
2698    drmmode_output_private_ptr drmmode_output = output->driver_private;
2699    drmModeConnectorPtr koutput = drmmode_output->mode_output;
2700    drmmode_ptr drmmode = drmmode_output->drmmode;
2701    int i;
2702    DisplayModePtr Modes = NULL, Mode;
2703    xf86MonPtr mon = NULL;
2704
2705    if (!koutput)
2706        return NULL;
2707
2708    drmModeFreePropertyBlob(drmmode_output->edid_blob);
2709
2710    /* look for an EDID property */
2711    drmmode_output->edid_blob =
2712        koutput_get_prop_blob(drmmode->fd, koutput, "EDID");
2713
2714    if (drmmode_output->edid_blob) {
2715        mon = xf86InterpretEDID(output->scrn->scrnIndex,
2716                                drmmode_output->edid_blob->data);
2717        if (mon && drmmode_output->edid_blob->length > 128)
2718            mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
2719    }
2720    xf86OutputSetEDID(output, mon);
2721
2722    drmmode_output_attach_tile(output);
2723
2724    /* modes should already be available */
2725    for (i = 0; i < koutput->count_modes; i++) {
2726        Mode = xnfalloc(sizeof(DisplayModeRec));
2727
2728        drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode);
2729        Modes = xf86ModesAdd(Modes, Mode);
2730
2731    }
2732
2733    return drmmode_output_add_gtf_modes(output, Modes);
2734}
2735
2736static void
2737drmmode_output_destroy(xf86OutputPtr output)
2738{
2739    drmmode_output_private_ptr drmmode_output = output->driver_private;
2740    int i;
2741
2742    drmModeFreePropertyBlob(drmmode_output->edid_blob);
2743    drmModeFreePropertyBlob(drmmode_output->tile_blob);
2744
2745    for (i = 0; i < drmmode_output->num_props; i++) {
2746        drmModeFreeProperty(drmmode_output->props[i].mode_prop);
2747        free(drmmode_output->props[i].atoms);
2748    }
2749    free(drmmode_output->props);
2750    if (drmmode_output->mode_output) {
2751        for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
2752            drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
2753        }
2754        drmModeFreeConnector(drmmode_output->mode_output);
2755    }
2756    free(drmmode_output->mode_encoders);
2757    free(drmmode_output);
2758    output->driver_private = NULL;
2759}
2760
2761static void
2762drmmode_output_dpms(xf86OutputPtr output, int mode)
2763{
2764    modesettingPtr ms = modesettingPTR(output->scrn);
2765    drmmode_output_private_ptr drmmode_output = output->driver_private;
2766    drmmode_ptr drmmode = drmmode_output->drmmode;
2767    xf86CrtcPtr crtc = output->crtc;
2768    drmModeConnectorPtr koutput = drmmode_output->mode_output;
2769
2770    if (!koutput)
2771        return;
2772
2773    /* XXX Check if DPMS mode is already the right one */
2774
2775    drmmode_output->dpms = mode;
2776
2777    if (ms->atomic_modeset) {
2778        if (mode != DPMSModeOn && !ms->pending_modeset)
2779            drmmode_output_disable(output);
2780    } else {
2781        drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
2782                                    drmmode_output->dpms_enum_id, mode);
2783    }
2784
2785    if (crtc) {
2786        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
2787
2788        if (mode == DPMSModeOn) {
2789            if (drmmode_crtc->need_modeset)
2790                drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
2791                                       crtc->x, crtc->y);
2792
2793            if (drmmode_crtc->enable_flipping)
2794                drmmode_InitSharedPixmapFlipping(crtc, drmmode_crtc->drmmode);
2795        } else {
2796            if (drmmode_crtc->enable_flipping)
2797                drmmode_FiniSharedPixmapFlipping(crtc, drmmode_crtc->drmmode);
2798        }
2799    }
2800
2801    return;
2802}
2803
2804static Bool
2805drmmode_property_ignore(drmModePropertyPtr prop)
2806{
2807    if (!prop)
2808        return TRUE;
2809    /* ignore blob prop */
2810    if (prop->flags & DRM_MODE_PROP_BLOB)
2811        return TRUE;
2812    /* ignore standard property */
2813    if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS") ||
2814        !strcmp(prop->name, "CRTC_ID"))
2815        return TRUE;
2816
2817    return FALSE;
2818}
2819
2820static void
2821drmmode_output_create_resources(xf86OutputPtr output)
2822{
2823    drmmode_output_private_ptr drmmode_output = output->driver_private;
2824    drmModeConnectorPtr mode_output = drmmode_output->mode_output;
2825    drmmode_ptr drmmode = drmmode_output->drmmode;
2826    drmModePropertyPtr drmmode_prop;
2827    int i, j, err;
2828
2829    drmmode_output->props =
2830        calloc(mode_output->count_props, sizeof(drmmode_prop_rec));
2831    if (!drmmode_output->props)
2832        return;
2833
2834    drmmode_output->num_props = 0;
2835    for (i = 0, j = 0; i < mode_output->count_props; i++) {
2836        drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]);
2837        if (drmmode_property_ignore(drmmode_prop)) {
2838            drmModeFreeProperty(drmmode_prop);
2839            continue;
2840        }
2841        drmmode_output->props[j].mode_prop = drmmode_prop;
2842        drmmode_output->props[j].value = mode_output->prop_values[i];
2843        drmmode_output->num_props++;
2844        j++;
2845    }
2846
2847    /* Create CONNECTOR_ID property */
2848    {
2849        Atom    name = MakeAtom("CONNECTOR_ID", 12, TRUE);
2850        INT32   value = mode_output->connector_id;
2851
2852        if (name != BAD_RESOURCE) {
2853            err = RRConfigureOutputProperty(output->randr_output, name,
2854                                            FALSE, FALSE, TRUE,
2855                                            1, &value);
2856            if (err != 0) {
2857                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2858                           "RRConfigureOutputProperty error, %d\n", err);
2859            }
2860            err = RRChangeOutputProperty(output->randr_output, name,
2861                                         XA_INTEGER, 32, PropModeReplace, 1,
2862                                         &value, FALSE, FALSE);
2863            if (err != 0) {
2864                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2865                           "RRChangeOutputProperty error, %d\n", err);
2866            }
2867        }
2868    }
2869
2870    if (drmmode->use_ctm) {
2871        Atom name = MakeAtom("CTM", 3, TRUE);
2872
2873        if (name != BAD_RESOURCE) {
2874            drmmode_output->ctm_atom = name;
2875
2876            err = RRConfigureOutputProperty(output->randr_output, name,
2877                                            FALSE, FALSE, TRUE, 0, NULL);
2878            if (err != 0) {
2879                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2880                           "RRConfigureOutputProperty error, %d\n", err);
2881            }
2882
2883            err = RRChangeOutputProperty(output->randr_output, name,
2884                                         XA_INTEGER, 32, PropModeReplace, 18,
2885                                         &ctm_identity, FALSE, FALSE);
2886            if (err != 0) {
2887                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2888                           "RRChangeOutputProperty error, %d\n", err);
2889            }
2890
2891            drmmode_output->ctm = ctm_identity;
2892        }
2893    }
2894
2895    for (i = 0; i < drmmode_output->num_props; i++) {
2896        drmmode_prop_ptr p = &drmmode_output->props[i];
2897
2898        drmmode_prop = p->mode_prop;
2899
2900        if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) {
2901            INT32 prop_range[2];
2902            INT32 value = p->value;
2903
2904            p->num_atoms = 1;
2905            p->atoms = calloc(p->num_atoms, sizeof(Atom));
2906            if (!p->atoms)
2907                continue;
2908            p->atoms[0] =
2909                MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
2910            prop_range[0] = drmmode_prop->values[0];
2911            prop_range[1] = drmmode_prop->values[1];
2912            err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
2913                                            FALSE, TRUE,
2914                                            drmmode_prop->
2915                                            flags & DRM_MODE_PROP_IMMUTABLE ?
2916                                            TRUE : FALSE, 2, prop_range);
2917            if (err != 0) {
2918                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2919                           "RRConfigureOutputProperty error, %d\n", err);
2920            }
2921            err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
2922                                         XA_INTEGER, 32, PropModeReplace, 1,
2923                                         &value, FALSE, TRUE);
2924            if (err != 0) {
2925                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2926                           "RRChangeOutputProperty error, %d\n", err);
2927            }
2928        }
2929        else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) {
2930            p->num_atoms = drmmode_prop->count_enums + 1;
2931            p->atoms = calloc(p->num_atoms, sizeof(Atom));
2932            if (!p->atoms)
2933                continue;
2934            p->atoms[0] =
2935                MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE);
2936            for (j = 1; j <= drmmode_prop->count_enums; j++) {
2937                struct drm_mode_property_enum *e = &drmmode_prop->enums[j - 1];
2938
2939                p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE);
2940            }
2941            err = RRConfigureOutputProperty(output->randr_output, p->atoms[0],
2942                                            FALSE, FALSE,
2943                                            drmmode_prop->
2944                                            flags & DRM_MODE_PROP_IMMUTABLE ?
2945                                            TRUE : FALSE, p->num_atoms - 1,
2946                                            (INT32 *) &p->atoms[1]);
2947            if (err != 0) {
2948                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2949                           "RRConfigureOutputProperty error, %d\n", err);
2950            }
2951            for (j = 0; j < drmmode_prop->count_enums; j++)
2952                if (drmmode_prop->enums[j].value == p->value)
2953                    break;
2954            /* there's always a matching value */
2955            err = RRChangeOutputProperty(output->randr_output, p->atoms[0],
2956                                         XA_ATOM, 32, PropModeReplace, 1,
2957                                         &p->atoms[j + 1], FALSE, TRUE);
2958            if (err != 0) {
2959                xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
2960                           "RRChangeOutputProperty error, %d\n", err);
2961            }
2962        }
2963    }
2964}
2965
2966static Bool
2967drmmode_output_set_property(xf86OutputPtr output, Atom property,
2968                            RRPropertyValuePtr value)
2969{
2970    drmmode_output_private_ptr drmmode_output = output->driver_private;
2971    drmmode_ptr drmmode = drmmode_output->drmmode;
2972    int i;
2973
2974    for (i = 0; i < drmmode_output->num_props; i++) {
2975        drmmode_prop_ptr p = &drmmode_output->props[i];
2976
2977        if (p->atoms[0] != property)
2978            continue;
2979
2980        if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) {
2981            uint32_t val;
2982
2983            if (value->type != XA_INTEGER || value->format != 32 ||
2984                value->size != 1)
2985                return FALSE;
2986            val = *(uint32_t *) value->data;
2987
2988            drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id,
2989                                        p->mode_prop->prop_id, (uint64_t) val);
2990            return TRUE;
2991        }
2992        else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) {
2993            Atom atom;
2994            const char *name;
2995            int j;
2996
2997            if (value->type != XA_ATOM || value->format != 32 ||
2998                value->size != 1)
2999                return FALSE;
3000            memcpy(&atom, value->data, 4);
3001            if (!(name = NameForAtom(atom)))
3002                return FALSE;
3003
3004            /* search for matching name string, then set its value down */
3005            for (j = 0; j < p->mode_prop->count_enums; j++) {
3006                if (!strcmp(p->mode_prop->enums[j].name, name)) {
3007                    drmModeConnectorSetProperty(drmmode->fd,
3008                                                drmmode_output->output_id,
3009                                                p->mode_prop->prop_id,
3010                                                p->mode_prop->enums[j].value);
3011                    return TRUE;
3012                }
3013            }
3014        }
3015    }
3016
3017    if (property == drmmode_output->ctm_atom) {
3018        const size_t matrix_size = sizeof(drmmode_output->ctm);
3019
3020        if (value->type != XA_INTEGER || value->format != 32 ||
3021            value->size * 4 != matrix_size)
3022            return FALSE;
3023
3024        memcpy(&drmmode_output->ctm, value->data, matrix_size);
3025
3026        // Update the CRTC if there is one bound to this output.
3027        if (output->crtc) {
3028            drmmode_set_ctm(output->crtc, &drmmode_output->ctm);
3029        }
3030    }
3031
3032    return TRUE;
3033}
3034
3035static Bool
3036drmmode_output_get_property(xf86OutputPtr output, Atom property)
3037{
3038    return TRUE;
3039}
3040
3041static const xf86OutputFuncsRec drmmode_output_funcs = {
3042    .dpms = drmmode_output_dpms,
3043    .create_resources = drmmode_output_create_resources,
3044    .set_property = drmmode_output_set_property,
3045    .get_property = drmmode_output_get_property,
3046    .detect = drmmode_output_detect,
3047    .mode_valid = drmmode_output_mode_valid,
3048
3049    .get_modes = drmmode_output_get_modes,
3050    .destroy = drmmode_output_destroy
3051};
3052
3053static int subpixel_conv_table[7] = {
3054    0,
3055    SubPixelUnknown,
3056    SubPixelHorizontalRGB,
3057    SubPixelHorizontalBGR,
3058    SubPixelVerticalRGB,
3059    SubPixelVerticalBGR,
3060    SubPixelNone
3061};
3062
3063static const char *const output_names[] = {
3064    "None",
3065    "VGA",
3066    "DVI-I",
3067    "DVI-D",
3068    "DVI-A",
3069    "Composite",
3070    "SVIDEO",
3071    "LVDS",
3072    "Component",
3073    "DIN",
3074    "DP",
3075    "HDMI",
3076    "HDMI-B",
3077    "TV",
3078    "eDP",
3079    "Virtual",
3080    "DSI",
3081    "DPI",
3082};
3083
3084static xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
3085{
3086    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3087    int i;
3088    for (i = 0; i < xf86_config->num_output; i++) {
3089        xf86OutputPtr output = xf86_config->output[i];
3090        drmmode_output_private_ptr drmmode_output;
3091
3092        drmmode_output = output->driver_private;
3093        if (drmmode_output->output_id == id)
3094            return output;
3095    }
3096    return NULL;
3097}
3098
3099static int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
3100{
3101    char *conn;
3102    char conn_id[5];
3103    int id, len;
3104    char *blob_data;
3105
3106    if (!path_blob)
3107        return -1;
3108
3109    blob_data = path_blob->data;
3110    /* we only handle MST paths for now */
3111    if (strncmp(blob_data, "mst:", 4))
3112        return -1;
3113
3114    conn = strchr(blob_data + 4, '-');
3115    if (!conn)
3116        return -1;
3117    len = conn - (blob_data + 4);
3118    if (len + 1 >= sizeof(conn_id))
3119        return -1;
3120    memcpy(conn_id, blob_data + 4, len);
3121    conn_id[len] = '\0';
3122    id = strtoul(conn_id, NULL, 10);
3123
3124    *conn_base_id = id;
3125
3126    *path = conn + 1;
3127    return 0;
3128}
3129
3130static void
3131drmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
3132		    drmModePropertyBlobPtr path_blob)
3133{
3134    int ret;
3135    char *extra_path;
3136    int conn_id;
3137    xf86OutputPtr output;
3138
3139    ret = parse_path_blob(path_blob, &conn_id, &extra_path);
3140    if (ret == -1)
3141        goto fallback;
3142
3143    output = find_output(pScrn, conn_id);
3144    if (!output)
3145        goto fallback;
3146
3147    snprintf(name, 32, "%s-%s", output->name, extra_path);
3148    return;
3149
3150 fallback:
3151    if (koutput->connector_type >= ARRAY_SIZE(output_names))
3152        snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id);
3153    else if (pScrn->is_gpu)
3154        snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id);
3155    else
3156        snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
3157}
3158
3159static Bool
3160drmmode_connector_check_vrr_capable(uint32_t drm_fd, int connector_id)
3161{
3162    uint32_t i;
3163    Bool found = FALSE;
3164    uint64_t prop_value = 0;
3165    drmModeObjectPropertiesPtr props;
3166    const char* prop_name = "VRR_CAPABLE";
3167
3168    props = drmModeObjectGetProperties(drm_fd, connector_id,
3169                                    DRM_MODE_OBJECT_CONNECTOR);
3170
3171    for (i = 0; !found && i < props->count_props; ++i) {
3172        drmModePropertyPtr drm_prop = drmModeGetProperty(drm_fd, props->props[i]);
3173
3174        if (!drm_prop)
3175            continue;
3176
3177        if (strcasecmp(drm_prop->name, prop_name) == 0) {
3178            prop_value = props->prop_values[i];
3179            found = TRUE;
3180        }
3181
3182        drmModeFreeProperty(drm_prop);
3183    }
3184
3185    drmModeFreeObjectProperties(props);
3186
3187    if(found)
3188        return prop_value ? TRUE : FALSE;
3189
3190    return FALSE;
3191}
3192
3193static unsigned int
3194drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
3195{
3196    xf86OutputPtr output;
3197    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3198    modesettingPtr ms = modesettingPTR(pScrn);
3199    drmModeConnectorPtr koutput;
3200    drmModeEncoderPtr *kencoders = NULL;
3201    drmmode_output_private_ptr drmmode_output;
3202    char name[32];
3203    int i;
3204    Bool nonDesktop = FALSE;
3205    drmModePropertyBlobPtr path_blob = NULL;
3206    const char *s;
3207    drmModeObjectPropertiesPtr props;
3208    static const drmmode_prop_info_rec connector_props[] = {
3209        [DRMMODE_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
3210    };
3211
3212    koutput =
3213        drmModeGetConnector(drmmode->fd, mode_res->connectors[num]);
3214    if (!koutput)
3215        return 0;
3216
3217    path_blob = koutput_get_prop_blob(drmmode->fd, koutput, "PATH");
3218    i = koutput_get_prop_idx(drmmode->fd, koutput, DRM_MODE_PROP_RANGE, RR_PROPERTY_NON_DESKTOP);
3219    if (i >= 0)
3220        nonDesktop = koutput->prop_values[i] != 0;
3221
3222    drmmode_create_name(pScrn, koutput, name, path_blob);
3223
3224    if (path_blob)
3225        drmModeFreePropertyBlob(path_blob);
3226
3227    if (path_blob && dynamic) {
3228        /* see if we have an output with this name already
3229           and hook stuff up */
3230        for (i = 0; i < xf86_config->num_output; i++) {
3231            output = xf86_config->output[i];
3232
3233            if (strncmp(output->name, name, 32))
3234                continue;
3235
3236            drmmode_output = output->driver_private;
3237            drmmode_output->output_id = mode_res->connectors[num];
3238            drmmode_output->mode_output = koutput;
3239            output->non_desktop = nonDesktop;
3240            return 1;
3241        }
3242    }
3243
3244    kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
3245    if (!kencoders) {
3246        goto out_free_encoders;
3247    }
3248
3249    for (i = 0; i < koutput->count_encoders; i++) {
3250        kencoders[i] = drmModeGetEncoder(drmmode->fd, koutput->encoders[i]);
3251        if (!kencoders[i]) {
3252            goto out_free_encoders;
3253        }
3254    }
3255
3256    if (xf86IsEntityShared(pScrn->entityList[0])) {
3257        if ((s = xf86GetOptValString(drmmode->Options, OPTION_ZAPHOD_HEADS))) {
3258            if (!drmmode_zaphod_string_matches(pScrn, s, name))
3259                goto out_free_encoders;
3260        } else {
3261            if (!drmmode->is_secondary && (num != 0))
3262                goto out_free_encoders;
3263            else if (drmmode->is_secondary && (num != 1))
3264                goto out_free_encoders;
3265        }
3266    }
3267
3268    output = xf86OutputCreate(pScrn, &drmmode_output_funcs, name);
3269    if (!output) {
3270        goto out_free_encoders;
3271    }
3272
3273    drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1);
3274    if (!drmmode_output) {
3275        xf86OutputDestroy(output);
3276        goto out_free_encoders;
3277    }
3278
3279    drmmode_output->output_id = mode_res->connectors[num];
3280    drmmode_output->mode_output = koutput;
3281    drmmode_output->mode_encoders = kencoders;
3282    drmmode_output->drmmode = drmmode;
3283    output->mm_width = koutput->mmWidth;
3284    output->mm_height = koutput->mmHeight;
3285
3286    output->subpixel_order = subpixel_conv_table[koutput->subpixel];
3287    output->interlaceAllowed = TRUE;
3288    output->doubleScanAllowed = TRUE;
3289    output->driver_private = drmmode_output;
3290    output->non_desktop = nonDesktop;
3291
3292    output->possible_crtcs = 0;
3293    for (i = 0; i < koutput->count_encoders; i++) {
3294        output->possible_crtcs |= (kencoders[i]->possible_crtcs >> crtcshift) & 0x7f;
3295    }
3296    /* work out the possible clones later */
3297    output->possible_clones = 0;
3298
3299    if (ms->atomic_modeset) {
3300        if (!drmmode_prop_info_copy(drmmode_output->props_connector,
3301                                    connector_props, DRMMODE_CONNECTOR__COUNT,
3302                                    0)) {
3303            goto out_free_encoders;
3304        }
3305        props = drmModeObjectGetProperties(drmmode->fd,
3306                                           drmmode_output->output_id,
3307                                           DRM_MODE_OBJECT_CONNECTOR);
3308        drmmode_prop_info_update(drmmode, drmmode_output->props_connector,
3309                                 DRMMODE_CONNECTOR__COUNT, props);
3310    } else {
3311        drmmode_output->dpms_enum_id =
3312            koutput_get_prop_id(drmmode->fd, koutput, DRM_MODE_PROP_ENUM,
3313                                "DPMS");
3314    }
3315
3316    if (dynamic) {
3317        output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
3318        if (output->randr_output) {
3319            drmmode_output_create_resources(output);
3320            RRPostPendingProperties(output->randr_output);
3321        }
3322    }
3323
3324    ms->is_connector_vrr_capable |=
3325	         drmmode_connector_check_vrr_capable(drmmode->fd,
3326                                                  drmmode_output->output_id);
3327    return 1;
3328
3329 out_free_encoders:
3330    if (kencoders) {
3331        for (i = 0; i < koutput->count_encoders; i++)
3332            drmModeFreeEncoder(kencoders[i]);
3333        free(kencoders);
3334    }
3335    drmModeFreeConnector(koutput);
3336
3337    return 0;
3338}
3339
3340static uint32_t
3341find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
3342{
3343    drmmode_output_private_ptr drmmode_output =
3344        output->driver_private, clone_drmout;
3345    int i;
3346    xf86OutputPtr clone_output;
3347    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3348    int index_mask = 0;
3349
3350    if (drmmode_output->enc_clone_mask == 0)
3351        return index_mask;
3352
3353    for (i = 0; i < xf86_config->num_output; i++) {
3354        clone_output = xf86_config->output[i];
3355        clone_drmout = clone_output->driver_private;
3356        if (output == clone_output)
3357            continue;
3358
3359        if (clone_drmout->enc_mask == 0)
3360            continue;
3361        if (drmmode_output->enc_clone_mask == clone_drmout->enc_mask)
3362            index_mask |= (1 << i);
3363    }
3364    return index_mask;
3365}
3366
3367static void
3368drmmode_clones_init(ScrnInfoPtr scrn, drmmode_ptr drmmode, drmModeResPtr mode_res)
3369{
3370    int i, j;
3371    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3372
3373    for (i = 0; i < xf86_config->num_output; i++) {
3374        xf86OutputPtr output = xf86_config->output[i];
3375        drmmode_output_private_ptr drmmode_output;
3376
3377        drmmode_output = output->driver_private;
3378        drmmode_output->enc_clone_mask = 0xff;
3379        /* and all the possible encoder clones for this output together */
3380        for (j = 0; j < drmmode_output->mode_output->count_encoders; j++) {
3381            int k;
3382
3383            for (k = 0; k < mode_res->count_encoders; k++) {
3384                if (mode_res->encoders[k] ==
3385                    drmmode_output->mode_encoders[j]->encoder_id)
3386                    drmmode_output->enc_mask |= (1 << k);
3387            }
3388
3389            drmmode_output->enc_clone_mask &=
3390                drmmode_output->mode_encoders[j]->possible_clones;
3391        }
3392    }
3393
3394    for (i = 0; i < xf86_config->num_output; i++) {
3395        xf86OutputPtr output = xf86_config->output[i];
3396
3397        output->possible_clones = find_clones(scrn, output);
3398    }
3399}
3400
3401static Bool
3402drmmode_set_pixmap_bo(drmmode_ptr drmmode, PixmapPtr pixmap, drmmode_bo *bo)
3403{
3404#ifdef GLAMOR_HAS_GBM
3405    ScrnInfoPtr scrn = drmmode->scrn;
3406    modesettingPtr ms = modesettingPTR(scrn);
3407
3408    if (!drmmode->glamor)
3409        return TRUE;
3410
3411    if (!ms->glamor.egl_create_textured_pixmap_from_gbm_bo(pixmap, bo->gbm,
3412                                                           bo->used_modifiers)) {
3413        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "Failed to create pixmap\n");
3414        return FALSE;
3415    }
3416#endif
3417
3418    return TRUE;
3419}
3420
3421Bool
3422drmmode_glamor_handle_new_screen_pixmap(drmmode_ptr drmmode)
3423{
3424    ScreenPtr screen = xf86ScrnToScreen(drmmode->scrn);
3425    PixmapPtr screen_pixmap = screen->GetScreenPixmap(screen);
3426
3427    if (!drmmode_set_pixmap_bo(drmmode, screen_pixmap, &drmmode->front_bo))
3428        return FALSE;
3429
3430    return TRUE;
3431}
3432
3433static Bool
3434drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
3435{
3436    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
3437    modesettingPtr ms = modesettingPTR(scrn);
3438    drmmode_ptr drmmode = &ms->drmmode;
3439    drmmode_bo old_front;
3440    ScreenPtr screen = xf86ScrnToScreen(scrn);
3441    uint32_t old_fb_id;
3442    int i, pitch, old_width, old_height, old_pitch;
3443    int cpp = (scrn->bitsPerPixel + 7) / 8;
3444    int kcpp = (drmmode->kbpp + 7) / 8;
3445    PixmapPtr ppix = screen->GetScreenPixmap(screen);
3446    void *new_pixels = NULL;
3447
3448    if (scrn->virtualX == width && scrn->virtualY == height)
3449        return TRUE;
3450
3451    xf86DrvMsg(scrn->scrnIndex, X_INFO,
3452               "Allocate new frame buffer %dx%d stride\n", width, height);
3453
3454    old_width = scrn->virtualX;
3455    old_height = scrn->virtualY;
3456    old_pitch = drmmode_bo_get_pitch(&drmmode->front_bo);
3457    old_front = drmmode->front_bo;
3458    old_fb_id = drmmode->fb_id;
3459    drmmode->fb_id = 0;
3460
3461    if (!drmmode_create_bo(drmmode, &drmmode->front_bo,
3462                           width, height, drmmode->kbpp))
3463        goto fail;
3464
3465    pitch = drmmode_bo_get_pitch(&drmmode->front_bo);
3466
3467    scrn->virtualX = width;
3468    scrn->virtualY = height;
3469    scrn->displayWidth = pitch / kcpp;
3470
3471    if (!drmmode->gbm) {
3472        new_pixels = drmmode_map_front_bo(drmmode);
3473        if (!new_pixels)
3474            goto fail;
3475    }
3476
3477    if (drmmode->shadow_enable) {
3478        uint32_t size = scrn->displayWidth * scrn->virtualY * cpp;
3479        new_pixels = calloc(1, size);
3480        if (new_pixels == NULL)
3481            goto fail;
3482        free(drmmode->shadow_fb);
3483        drmmode->shadow_fb = new_pixels;
3484    }
3485
3486    if (drmmode->shadow_enable2) {
3487        uint32_t size = scrn->displayWidth * scrn->virtualY * cpp;
3488        void *fb2 = calloc(1, size);
3489        free(drmmode->shadow_fb2);
3490        drmmode->shadow_fb2 = fb2;
3491    }
3492
3493    screen->ModifyPixmapHeader(ppix, width, height, -1, -1,
3494                               scrn->displayWidth * cpp, new_pixels);
3495
3496    if (!drmmode_glamor_handle_new_screen_pixmap(drmmode))
3497        goto fail;
3498
3499    drmmode_clear_pixmap(ppix);
3500
3501    for (i = 0; i < xf86_config->num_crtc; i++) {
3502        xf86CrtcPtr crtc = xf86_config->crtc[i];
3503
3504        if (!crtc->enabled)
3505            continue;
3506
3507        drmmode_set_mode_major(crtc, &crtc->mode,
3508                               crtc->rotation, crtc->x, crtc->y);
3509    }
3510
3511    if (old_fb_id)
3512        drmModeRmFB(drmmode->fd, old_fb_id);
3513
3514    drmmode_bo_destroy(drmmode, &old_front);
3515
3516    return TRUE;
3517
3518 fail:
3519    drmmode_bo_destroy(drmmode, &drmmode->front_bo);
3520    drmmode->front_bo = old_front;
3521    scrn->virtualX = old_width;
3522    scrn->virtualY = old_height;
3523    scrn->displayWidth = old_pitch / kcpp;
3524    drmmode->fb_id = old_fb_id;
3525
3526    return FALSE;
3527}
3528
3529static void
3530drmmode_validate_leases(ScrnInfoPtr scrn)
3531{
3532    ScreenPtr screen = scrn->pScreen;
3533    rrScrPrivPtr scr_priv;
3534    modesettingPtr ms = modesettingPTR(scrn);
3535    drmmode_ptr drmmode = &ms->drmmode;
3536    drmModeLesseeListPtr lessees;
3537    RRLeasePtr lease, next;
3538    int l;
3539
3540    /* Bail out if RandR wasn't initialized. */
3541    if (!dixPrivateKeyRegistered(rrPrivKey))
3542        return;
3543
3544    scr_priv = rrGetScrPriv(screen);
3545
3546    /* We can't talk to the kernel about leases when VT switched */
3547    if (!scrn->vtSema)
3548        return;
3549
3550    lessees = drmModeListLessees(drmmode->fd);
3551    if (!lessees)
3552        return;
3553
3554    xorg_list_for_each_entry_safe(lease, next, &scr_priv->leases, list) {
3555        drmmode_lease_private_ptr lease_private = lease->devPrivate;
3556
3557        for (l = 0; l < lessees->count; l++) {
3558            if (lessees->lessees[l] == lease_private->lessee_id)
3559                break;
3560        }
3561
3562        /* check to see if the lease has gone away */
3563        if (l == lessees->count) {
3564            free(lease_private);
3565            lease->devPrivate = NULL;
3566            xf86CrtcLeaseTerminated(lease);
3567        }
3568    }
3569
3570    free(lessees);
3571}
3572
3573static int
3574drmmode_create_lease(RRLeasePtr lease, int *fd)
3575{
3576    ScreenPtr screen = lease->screen;
3577    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3578    modesettingPtr ms = modesettingPTR(scrn);
3579    drmmode_ptr drmmode = &ms->drmmode;
3580    int ncrtc = lease->numCrtcs;
3581    int noutput = lease->numOutputs;
3582    int nobjects;
3583    int c, o;
3584    int i;
3585    int lease_fd;
3586    uint32_t *objects;
3587    drmmode_lease_private_ptr   lease_private;
3588
3589    nobjects = ncrtc + noutput;
3590
3591    if (ms->atomic_modeset)
3592        nobjects += ncrtc; /* account for planes as well */
3593
3594    if (nobjects == 0)
3595        return BadValue;
3596
3597    lease_private = calloc(1, sizeof (drmmode_lease_private_rec));
3598    if (!lease_private)
3599        return BadAlloc;
3600
3601    objects = xallocarray(nobjects, sizeof (uint32_t));
3602
3603    if (!objects) {
3604        free(lease_private);
3605        return BadAlloc;
3606    }
3607
3608    i = 0;
3609
3610    /* Add CRTC and plane ids */
3611    for (c = 0; c < ncrtc; c++) {
3612        xf86CrtcPtr crtc = lease->crtcs[c]->devPrivate;
3613        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3614
3615        objects[i++] = drmmode_crtc->mode_crtc->crtc_id;
3616        if (ms->atomic_modeset)
3617            objects[i++] = drmmode_crtc->plane_id;
3618    }
3619
3620    /* Add connector ids */
3621
3622    for (o = 0; o < noutput; o++) {
3623        xf86OutputPtr   output = lease->outputs[o]->devPrivate;
3624        drmmode_output_private_ptr drmmode_output = output->driver_private;
3625
3626        objects[i++] = drmmode_output->mode_output->connector_id;
3627    }
3628
3629    /* call kernel to create lease */
3630    assert (i == nobjects);
3631
3632    lease_fd = drmModeCreateLease(drmmode->fd, objects, nobjects, 0, &lease_private->lessee_id);
3633
3634    free(objects);
3635
3636    if (lease_fd < 0) {
3637        free(lease_private);
3638        return BadMatch;
3639    }
3640
3641    lease->devPrivate = lease_private;
3642
3643    xf86CrtcLeaseStarted(lease);
3644
3645    *fd = lease_fd;
3646    return Success;
3647}
3648
3649static void
3650drmmode_terminate_lease(RRLeasePtr lease)
3651{
3652    ScreenPtr screen = lease->screen;
3653    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
3654    modesettingPtr ms = modesettingPTR(scrn);
3655    drmmode_ptr drmmode = &ms->drmmode;
3656    drmmode_lease_private_ptr lease_private = lease->devPrivate;
3657
3658    if (drmModeRevokeLease(drmmode->fd, lease_private->lessee_id) == 0) {
3659        free(lease_private);
3660        lease->devPrivate = NULL;
3661        xf86CrtcLeaseTerminated(lease);
3662    }
3663}
3664
3665static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
3666    .resize = drmmode_xf86crtc_resize,
3667    .create_lease = drmmode_create_lease,
3668    .terminate_lease = drmmode_terminate_lease
3669};
3670
3671Bool
3672drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp)
3673{
3674    modesettingEntPtr ms_ent = ms_ent_priv(pScrn);
3675    int i;
3676    int ret;
3677    uint64_t value = 0;
3678    unsigned int crtcs_needed = 0;
3679    drmModeResPtr mode_res;
3680    int crtcshift;
3681
3682    /* check for dumb capability */
3683    ret = drmGetCap(drmmode->fd, DRM_CAP_DUMB_BUFFER, &value);
3684    if (ret > 0 || value != 1) {
3685        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
3686                   "KMS doesn't support dumb interface\n");
3687        return FALSE;
3688    }
3689
3690    xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
3691
3692    drmmode->scrn = pScrn;
3693    drmmode->cpp = cpp;
3694    mode_res = drmModeGetResources(drmmode->fd);
3695    if (!mode_res)
3696        return FALSE;
3697
3698    crtcshift = ffs(ms_ent->assigned_crtcs ^ 0xffffffff) - 1;
3699    for (i = 0; i < mode_res->count_connectors; i++)
3700        crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, FALSE,
3701                                            crtcshift);
3702
3703    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, MS_LOGLEVEL_DEBUG,
3704                   "Up to %d crtcs needed for screen.\n", crtcs_needed);
3705
3706    xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
3707                         mode_res->max_height);
3708    for (i = 0; i < mode_res->count_crtcs; i++)
3709        if (!xf86IsEntityShared(pScrn->entityList[0]) ||
3710            (crtcs_needed && !(ms_ent->assigned_crtcs & (1 << i))))
3711            crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
3712
3713    /* All ZaphodHeads outputs provided with matching crtcs? */
3714    if (xf86IsEntityShared(pScrn->entityList[0]) && (crtcs_needed > 0))
3715        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
3716                   "%d ZaphodHeads crtcs unavailable. Some outputs will stay off.\n",
3717                   crtcs_needed);
3718
3719    /* workout clones */
3720    drmmode_clones_init(pScrn, drmmode, mode_res);
3721
3722    drmModeFreeResources(mode_res);
3723    xf86ProviderSetup(pScrn, NULL, "modesetting");
3724
3725    xf86InitialConfiguration(pScrn, TRUE);
3726
3727    return TRUE;
3728}
3729
3730Bool
3731drmmode_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
3732{
3733#ifdef GLAMOR_HAS_GBM
3734    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
3735    modesettingPtr ms = modesettingPTR(pScrn);
3736
3737    if (drmmode->glamor) {
3738        if (!ms->glamor.init(pScreen, GLAMOR_USE_EGL_SCREEN)) {
3739            return FALSE;
3740        }
3741#ifdef GBM_BO_WITH_MODIFIERS
3742        ms->glamor.set_drawable_modifiers_func(pScreen, get_drawable_modifiers);
3743#endif
3744    }
3745#endif
3746
3747    return TRUE;
3748}
3749
3750void
3751drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y)
3752{
3753    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3754    xf86OutputPtr output = config->output[config->compat_output];
3755    xf86CrtcPtr crtc = output->crtc;
3756
3757    if (crtc && crtc->enabled) {
3758        drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, x, y);
3759    }
3760}
3761
3762Bool
3763drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode, Bool set_hw,
3764                          Bool ign_err)
3765{
3766    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
3767    Bool success = TRUE;
3768    int c;
3769
3770    for (c = 0; c < config->num_crtc; c++) {
3771        xf86CrtcPtr crtc = config->crtc[c];
3772        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3773        xf86OutputPtr output = NULL;
3774        int o;
3775
3776        /* Skip disabled CRTCs */
3777        if (!crtc->enabled) {
3778            if (set_hw) {
3779                drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
3780                               0, 0, 0, NULL, 0, NULL);
3781            }
3782            continue;
3783        }
3784
3785        if (config->output[config->compat_output]->crtc == crtc)
3786            output = config->output[config->compat_output];
3787        else {
3788            for (o = 0; o < config->num_output; o++)
3789                if (config->output[o]->crtc == crtc) {
3790                    output = config->output[o];
3791                    break;
3792                }
3793        }
3794        /* paranoia */
3795        if (!output)
3796            continue;
3797
3798        /* Mark that we'll need to re-set the mode for sure */
3799        memset(&crtc->mode, 0, sizeof(crtc->mode));
3800        if (!crtc->desiredMode.CrtcHDisplay) {
3801            DisplayModePtr mode =
3802                xf86OutputFindClosestMode(output, pScrn->currentMode);
3803
3804            if (!mode)
3805                return FALSE;
3806            crtc->desiredMode = *mode;
3807            crtc->desiredRotation = RR_Rotate_0;
3808            crtc->desiredX = 0;
3809            crtc->desiredY = 0;
3810        }
3811
3812        if (set_hw) {
3813            if (!crtc->funcs->
3814                set_mode_major(crtc, &crtc->desiredMode, crtc->desiredRotation,
3815                               crtc->desiredX, crtc->desiredY)) {
3816                if (!ign_err)
3817                    return FALSE;
3818                else {
3819                    success = FALSE;
3820                    crtc->enabled = FALSE;
3821                    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
3822                               "Failed to set the desired mode on connector %s\n",
3823                               output->name);
3824                }
3825            }
3826        } else {
3827            crtc->mode = crtc->desiredMode;
3828            crtc->rotation = crtc->desiredRotation;
3829            crtc->x = crtc->desiredX;
3830            crtc->y = crtc->desiredY;
3831            if (!xf86CrtcRotate(crtc))
3832                return FALSE;
3833        }
3834    }
3835
3836    /* Validate leases on VT re-entry */
3837    drmmode_validate_leases(pScrn);
3838
3839    return success;
3840}
3841
3842static void
3843drmmode_load_palette(ScrnInfoPtr pScrn, int numColors,
3844                     int *indices, LOCO * colors, VisualPtr pVisual)
3845{
3846    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3847    uint16_t lut_r[256], lut_g[256], lut_b[256];
3848    int index, j, i;
3849    int c;
3850
3851    for (c = 0; c < xf86_config->num_crtc; c++) {
3852        xf86CrtcPtr crtc = xf86_config->crtc[c];
3853        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3854
3855        for (i = 0; i < 256; i++) {
3856            lut_r[i] = drmmode_crtc->lut_r[i] << 6;
3857            lut_g[i] = drmmode_crtc->lut_g[i] << 6;
3858            lut_b[i] = drmmode_crtc->lut_b[i] << 6;
3859        }
3860
3861        switch (pScrn->depth) {
3862        case 15:
3863            for (i = 0; i < numColors; i++) {
3864                index = indices[i];
3865                for (j = 0; j < 8; j++) {
3866                    lut_r[index * 8 + j] = colors[index].red << 6;
3867                    lut_g[index * 8 + j] = colors[index].green << 6;
3868                    lut_b[index * 8 + j] = colors[index].blue << 6;
3869                }
3870            }
3871            break;
3872        case 16:
3873            for (i = 0; i < numColors; i++) {
3874                index = indices[i];
3875
3876                if (i <= 31) {
3877                    for (j = 0; j < 8; j++) {
3878                        lut_r[index * 8 + j] = colors[index].red << 6;
3879                        lut_b[index * 8 + j] = colors[index].blue << 6;
3880                    }
3881                }
3882
3883                for (j = 0; j < 4; j++) {
3884                    lut_g[index * 4 + j] = colors[index].green << 6;
3885                }
3886            }
3887            break;
3888        default:
3889            for (i = 0; i < numColors; i++) {
3890                index = indices[i];
3891                lut_r[index] = colors[index].red << 6;
3892                lut_g[index] = colors[index].green << 6;
3893                lut_b[index] = colors[index].blue << 6;
3894            }
3895            break;
3896        }
3897
3898        /* Make the change through RandR */
3899        if (crtc->randr_crtc)
3900            RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
3901        else
3902            crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
3903    }
3904}
3905
3906static Bool
3907drmmode_crtc_upgrade_lut(xf86CrtcPtr crtc, int num)
3908{
3909    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
3910    uint64_t size;
3911
3912    if (!drmmode_crtc->use_gamma_lut)
3913        return TRUE;
3914
3915    assert(drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT_SIZE].prop_id);
3916
3917    size = drmmode_crtc->props[DRMMODE_CRTC_GAMMA_LUT_SIZE].value;
3918
3919    if (size != crtc->gamma_size) {
3920        ScrnInfoPtr pScrn = crtc->scrn;
3921        uint16_t *gamma = malloc(3 * size * sizeof(uint16_t));
3922
3923        if (gamma) {
3924            free(crtc->gamma_red);
3925
3926            crtc->gamma_size = size;
3927            crtc->gamma_red = gamma;
3928            crtc->gamma_green = gamma + size;
3929            crtc->gamma_blue = gamma + size * 2;
3930
3931            xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, MS_LOGLEVEL_DEBUG,
3932                           "Gamma ramp set to %" PRIu64 " entries on CRTC %d\n",
3933                           size, num);
3934        } else {
3935            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
3936                       "Failed to allocate memory for %" PRIu64 " gamma ramp entries "
3937                       "on CRTC %d.\n",
3938                       size, num);
3939            return FALSE;
3940        }
3941    }
3942
3943    return TRUE;
3944}
3945
3946Bool
3947drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
3948{
3949    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3950    int i;
3951
3952    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
3953              "Initializing kms color map for depth %d, %d bpc.\n",
3954              pScrn->depth, pScrn->rgbBits);
3955    if (!miCreateDefColormap(pScreen))
3956        return FALSE;
3957
3958    /* If the GAMMA_LUT property is available, replace the server's default
3959     * gamma ramps with ones of the appropriate size. */
3960    for (i = 0; i < xf86_config->num_crtc; i++)
3961        if (!drmmode_crtc_upgrade_lut(xf86_config->crtc[i], i))
3962            return FALSE;
3963
3964    /* Adapt color map size and depth to color depth of screen. */
3965    if (!xf86HandleColormaps(pScreen, 1 << pScrn->rgbBits, 10,
3966                             drmmode_load_palette, NULL,
3967                             CMAP_PALETTED_TRUECOLOR |
3968                             CMAP_RELOAD_ON_MODE_SWITCH))
3969        return FALSE;
3970    return TRUE;
3971}
3972
3973#define DRM_MODE_LINK_STATUS_GOOD       0
3974#define DRM_MODE_LINK_STATUS_BAD        1
3975
3976void
3977drmmode_update_kms_state(drmmode_ptr drmmode)
3978{
3979    ScrnInfoPtr scrn = drmmode->scrn;
3980    drmModeResPtr mode_res;
3981    xf86CrtcConfigPtr  config = XF86_CRTC_CONFIG_PTR(scrn);
3982    int i, j;
3983    Bool found = FALSE;
3984    Bool changed = FALSE;
3985
3986    /* Try to re-set the mode on all the connectors with a BAD link-state:
3987     * This may happen if a link degrades and a new modeset is necessary, using
3988     * different link-training parameters. If the kernel found that the current
3989     * mode is not achievable anymore, it should have pruned the mode before
3990     * sending the hotplug event. Try to re-set the currently-set mode to keep
3991     * the display alive, this will fail if the mode has been pruned.
3992     * In any case, we will send randr events for the Desktop Environment to
3993     * deal with it, if it wants to.
3994     */
3995    for (i = 0; i < config->num_output; i++) {
3996        xf86OutputPtr output = config->output[i];
3997        drmmode_output_private_ptr drmmode_output = output->driver_private;
3998
3999        drmmode_output_detect(output);
4000
4001        /* Get an updated view of the properties for the current connector and
4002         * look for the link-status property
4003         */
4004        for (j = 0; j < drmmode_output->num_props; j++) {
4005            drmmode_prop_ptr p = &drmmode_output->props[j];
4006
4007            if (!strcmp(p->mode_prop->name, "link-status")) {
4008                if (p->value == DRM_MODE_LINK_STATUS_BAD) {
4009                    xf86CrtcPtr crtc = output->crtc;
4010                    if (!crtc)
4011                        continue;
4012
4013                    /* the connector got a link failure, re-set the current mode */
4014                    drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
4015                                           crtc->x, crtc->y);
4016
4017                    xf86DrvMsg(scrn->scrnIndex, X_WARNING,
4018                               "hotplug event: connector %u's link-state is BAD, "
4019                               "tried resetting the current mode. You may be left"
4020                               "with a black screen if this fails...\n",
4021                               drmmode_output->mode_output->connector_id);
4022                }
4023                break;
4024            }
4025        }
4026    }
4027
4028    mode_res = drmModeGetResources(drmmode->fd);
4029    if (!mode_res)
4030        goto out;
4031
4032    if (mode_res->count_crtcs != config->num_crtc) {
4033        /* this triggers with Zaphod mode where we don't currently support connector hotplug or MST. */
4034        goto out_free_res;
4035    }
4036
4037    /* figure out if we have gotten rid of any connectors
4038       traverse old output list looking for outputs */
4039    for (i = 0; i < config->num_output; i++) {
4040        xf86OutputPtr output = config->output[i];
4041        drmmode_output_private_ptr drmmode_output;
4042
4043        drmmode_output = output->driver_private;
4044        found = FALSE;
4045        for (j = 0; j < mode_res->count_connectors; j++) {
4046            if (mode_res->connectors[j] == drmmode_output->output_id) {
4047                found = TRUE;
4048                break;
4049            }
4050        }
4051        if (found)
4052            continue;
4053
4054        drmModeFreeConnector(drmmode_output->mode_output);
4055        drmmode_output->mode_output = NULL;
4056        drmmode_output->output_id = -1;
4057
4058        changed = TRUE;
4059    }
4060
4061    /* find new output ids we don't have outputs for */
4062    for (i = 0; i < mode_res->count_connectors; i++) {
4063        found = FALSE;
4064
4065        for (j = 0; j < config->num_output; j++) {
4066            xf86OutputPtr output = config->output[j];
4067            drmmode_output_private_ptr drmmode_output;
4068
4069            drmmode_output = output->driver_private;
4070            if (mode_res->connectors[i] == drmmode_output->output_id) {
4071                found = TRUE;
4072                break;
4073            }
4074        }
4075        if (found)
4076            continue;
4077
4078        changed = TRUE;
4079        drmmode_output_init(scrn, drmmode, mode_res, i, TRUE, 0);
4080    }
4081
4082    if (changed) {
4083        RRSetChanged(xf86ScrnToScreen(scrn));
4084        RRTellChanged(xf86ScrnToScreen(scrn));
4085    }
4086
4087out_free_res:
4088
4089    /* Check to see if a lessee has disappeared */
4090    drmmode_validate_leases(scrn);
4091
4092    drmModeFreeResources(mode_res);
4093out:
4094    RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
4095}
4096
4097#undef DRM_MODE_LINK_STATUS_BAD
4098#undef DRM_MODE_LINK_STATUS_GOOD
4099
4100#ifdef CONFIG_UDEV_KMS
4101
4102static void
4103drmmode_handle_uevents(int fd, void *closure)
4104{
4105    drmmode_ptr drmmode = closure;
4106    struct udev_device *dev;
4107    Bool found = FALSE;
4108
4109    while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) {
4110        udev_device_unref(dev);
4111        found = TRUE;
4112    }
4113    if (!found)
4114        return;
4115
4116    drmmode_update_kms_state(drmmode);
4117}
4118
4119#endif
4120
4121void
4122drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode)
4123{
4124#ifdef CONFIG_UDEV_KMS
4125    struct udev *u;
4126    struct udev_monitor *mon;
4127
4128    u = udev_new();
4129    if (!u)
4130        return;
4131    mon = udev_monitor_new_from_netlink(u, "udev");
4132    if (!mon) {
4133        udev_unref(u);
4134        return;
4135    }
4136
4137    if (udev_monitor_filter_add_match_subsystem_devtype(mon,
4138                                                        "drm",
4139                                                        "drm_minor") < 0 ||
4140        udev_monitor_enable_receiving(mon) < 0) {
4141        udev_monitor_unref(mon);
4142        udev_unref(u);
4143        return;
4144    }
4145
4146    drmmode->uevent_handler =
4147        xf86AddGeneralHandler(udev_monitor_get_fd(mon),
4148                              drmmode_handle_uevents, drmmode);
4149
4150    drmmode->uevent_monitor = mon;
4151#endif
4152}
4153
4154void
4155drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode)
4156{
4157#ifdef CONFIG_UDEV_KMS
4158    if (drmmode->uevent_handler) {
4159        struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor);
4160
4161        xf86RemoveGeneralHandler(drmmode->uevent_handler);
4162
4163        udev_monitor_unref(drmmode->uevent_monitor);
4164        udev_unref(u);
4165    }
4166#endif
4167}
4168
4169/* create front and cursor BOs */
4170Bool
4171drmmode_create_initial_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
4172{
4173    modesettingPtr ms = modesettingPTR(pScrn);
4174    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
4175    int width;
4176    int height;
4177    int bpp = ms->drmmode.kbpp;
4178    int i;
4179    int cpp = (bpp + 7) / 8;
4180
4181    width = pScrn->virtualX;
4182    height = pScrn->virtualY;
4183
4184    if (!drmmode_create_bo(drmmode, &drmmode->front_bo, width, height, bpp))
4185        return FALSE;
4186    pScrn->displayWidth = drmmode_bo_get_pitch(&drmmode->front_bo) / cpp;
4187
4188    width = ms->cursor_width;
4189    height = ms->cursor_height;
4190    bpp = 32;
4191    for (i = 0; i < xf86_config->num_crtc; i++) {
4192        xf86CrtcPtr crtc = xf86_config->crtc[i];
4193        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
4194
4195        drmmode_crtc->cursor_bo =
4196            dumb_bo_create(drmmode->fd, width, height, bpp);
4197    }
4198    return TRUE;
4199}
4200
4201void *
4202drmmode_map_front_bo(drmmode_ptr drmmode)
4203{
4204    return drmmode_bo_map(drmmode, &drmmode->front_bo);
4205}
4206
4207void *
4208drmmode_map_secondary_bo(drmmode_ptr drmmode, msPixmapPrivPtr ppriv)
4209{
4210    int ret;
4211
4212    if (ppriv->backing_bo->ptr)
4213        return ppriv->backing_bo->ptr;
4214
4215    ret = dumb_bo_map(drmmode->fd, ppriv->backing_bo);
4216    if (ret)
4217        return NULL;
4218
4219    return ppriv->backing_bo->ptr;
4220}
4221
4222Bool
4223drmmode_map_cursor_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
4224{
4225    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
4226    int i, ret;
4227
4228    for (i = 0; i < xf86_config->num_crtc; i++) {
4229        xf86CrtcPtr crtc = xf86_config->crtc[i];
4230        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
4231
4232        ret = dumb_bo_map(drmmode->fd, drmmode_crtc->cursor_bo);
4233        if (ret)
4234            return FALSE;
4235    }
4236    return TRUE;
4237}
4238
4239void
4240drmmode_free_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode)
4241{
4242    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
4243    int i;
4244
4245    if (drmmode->fb_id) {
4246        drmModeRmFB(drmmode->fd, drmmode->fb_id);
4247        drmmode->fb_id = 0;
4248    }
4249
4250    drmmode_bo_destroy(drmmode, &drmmode->front_bo);
4251
4252    for (i = 0; i < xf86_config->num_crtc; i++) {
4253        xf86CrtcPtr crtc = xf86_config->crtc[i];
4254        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
4255
4256        dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo);
4257    }
4258}
4259
4260/* ugly workaround to see if we can create 32bpp */
4261void
4262drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int *depth,
4263                        int *bpp)
4264{
4265    drmModeResPtr mode_res;
4266    uint64_t value;
4267    struct dumb_bo *bo;
4268    uint32_t fb_id;
4269    int ret;
4270
4271    /* 16 is fine */
4272    ret = drmGetCap(drmmode->fd, DRM_CAP_DUMB_PREFERRED_DEPTH, &value);
4273    if (!ret && (value == 16 || value == 8)) {
4274        *depth = value;
4275        *bpp = value;
4276        return;
4277    }
4278
4279    *depth = 24;
4280    mode_res = drmModeGetResources(drmmode->fd);
4281    if (!mode_res)
4282        return;
4283
4284    if (mode_res->min_width == 0)
4285        mode_res->min_width = 1;
4286    if (mode_res->min_height == 0)
4287        mode_res->min_height = 1;
4288    /*create a bo */
4289    bo = dumb_bo_create(drmmode->fd, mode_res->min_width, mode_res->min_height,
4290                        32);
4291    if (!bo) {
4292        *bpp = 24;
4293        goto out;
4294    }
4295
4296    ret = drmModeAddFB(drmmode->fd, mode_res->min_width, mode_res->min_height,
4297                       24, 32, bo->pitch, bo->handle, &fb_id);
4298
4299    if (ret) {
4300        *bpp = 24;
4301        dumb_bo_destroy(drmmode->fd, bo);
4302        goto out;
4303    }
4304
4305    drmModeRmFB(drmmode->fd, fb_id);
4306    *bpp = 32;
4307
4308    dumb_bo_destroy(drmmode->fd, bo);
4309 out:
4310    drmModeFreeResources(mode_res);
4311    return;
4312}
4313
4314void
4315drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled)
4316{
4317    ScrnInfoPtr pScrn = crtc->scrn;
4318    modesettingPtr ms = modesettingPTR(pScrn);
4319    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
4320    drmmode_ptr drmmode = drmmode_crtc->drmmode;
4321
4322    if (drmmode->vrr_prop_id && drmmode_crtc->vrr_enabled != enabled &&
4323        drmModeObjectSetProperty(ms->fd,
4324                                 drmmode_crtc->mode_crtc->crtc_id,
4325                                 DRM_MODE_OBJECT_CRTC,
4326                                 drmmode->vrr_prop_id,
4327                                 enabled) == 0)
4328        drmmode_crtc->vrr_enabled = enabled;
4329}
4330
4331/*
4332 * We hook the screen's cursor-sprite (swcursor) functions to see if a swcursor
4333 * is active. When a swcursor is active we disable page-flipping.
4334 */
4335
4336static void drmmode_sprite_do_set_cursor(msSpritePrivPtr sprite_priv,
4337                                         ScrnInfoPtr scrn, int x, int y)
4338{
4339    modesettingPtr ms = modesettingPTR(scrn);
4340    CursorPtr cursor = sprite_priv->cursor;
4341    Bool sprite_visible = sprite_priv->sprite_visible;
4342
4343    if (cursor) {
4344        x -= cursor->bits->xhot;
4345        y -= cursor->bits->yhot;
4346
4347        sprite_priv->sprite_visible =
4348            x < scrn->virtualX && y < scrn->virtualY &&
4349            (x + cursor->bits->width > 0) &&
4350            (y + cursor->bits->height > 0);
4351    } else {
4352        sprite_priv->sprite_visible = FALSE;
4353    }
4354
4355    ms->drmmode.sprites_visible += sprite_priv->sprite_visible - sprite_visible;
4356}
4357
4358static void drmmode_sprite_set_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
4359                                      CursorPtr pCursor, int x, int y)
4360{
4361    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4362    modesettingPtr ms = modesettingPTR(scrn);
4363    msSpritePrivPtr sprite_priv = msGetSpritePriv(pDev, ms, pScreen);
4364
4365    sprite_priv->cursor = pCursor;
4366    drmmode_sprite_do_set_cursor(sprite_priv, scrn, x, y);
4367
4368    ms->SpriteFuncs->SetCursor(pDev, pScreen, pCursor, x, y);
4369}
4370
4371static void drmmode_sprite_move_cursor(DeviceIntPtr pDev, ScreenPtr pScreen,
4372                                       int x, int y)
4373{
4374    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4375    modesettingPtr ms = modesettingPTR(scrn);
4376    msSpritePrivPtr sprite_priv = msGetSpritePriv(pDev, ms, pScreen);
4377
4378    drmmode_sprite_do_set_cursor(sprite_priv, scrn, x, y);
4379
4380    ms->SpriteFuncs->MoveCursor(pDev, pScreen, x, y);
4381}
4382
4383static Bool drmmode_sprite_realize_realize_cursor(DeviceIntPtr pDev,
4384                                                  ScreenPtr pScreen,
4385                                                  CursorPtr pCursor)
4386{
4387    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4388    modesettingPtr ms = modesettingPTR(scrn);
4389
4390    return ms->SpriteFuncs->RealizeCursor(pDev, pScreen, pCursor);
4391}
4392
4393static Bool drmmode_sprite_realize_unrealize_cursor(DeviceIntPtr pDev,
4394                                                    ScreenPtr pScreen,
4395                                                    CursorPtr pCursor)
4396{
4397    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4398    modesettingPtr ms = modesettingPTR(scrn);
4399
4400    return ms->SpriteFuncs->UnrealizeCursor(pDev, pScreen, pCursor);
4401}
4402
4403static Bool drmmode_sprite_device_cursor_initialize(DeviceIntPtr pDev,
4404                                                    ScreenPtr pScreen)
4405{
4406    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4407    modesettingPtr ms = modesettingPTR(scrn);
4408
4409    return ms->SpriteFuncs->DeviceCursorInitialize(pDev, pScreen);
4410}
4411
4412static void drmmode_sprite_device_cursor_cleanup(DeviceIntPtr pDev,
4413                                                 ScreenPtr pScreen)
4414{
4415    ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
4416    modesettingPtr ms = modesettingPTR(scrn);
4417
4418    ms->SpriteFuncs->DeviceCursorCleanup(pDev, pScreen);
4419}
4420
4421miPointerSpriteFuncRec drmmode_sprite_funcs = {
4422    .RealizeCursor = drmmode_sprite_realize_realize_cursor,
4423    .UnrealizeCursor = drmmode_sprite_realize_unrealize_cursor,
4424    .SetCursor = drmmode_sprite_set_cursor,
4425    .MoveCursor = drmmode_sprite_move_cursor,
4426    .DeviceCursorInitialize = drmmode_sprite_device_cursor_initialize,
4427    .DeviceCursorCleanup = drmmode_sprite_device_cursor_cleanup,
4428};
4429