1/*
2 * Copyright © 2014 Intel Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_DIX_CONFIG_H
24#include "dix-config.h"
25#endif
26
27#include <xserver_poll.h>
28#include <xf86drm.h>
29
30#include "driver.h"
31
32/*
33 * Flush the DRM event queue when full; makes space for new events.
34 *
35 * Returns a negative value on error, 0 if there was nothing to process,
36 * or 1 if we handled any events.
37 */
38int
39ms_flush_drm_events(ScreenPtr screen)
40{
41    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
42    modesettingPtr ms = modesettingPTR(scrn);
43
44    struct pollfd p = { .fd = ms->fd, .events = POLLIN };
45    int r;
46
47    do {
48            r = xserver_poll(&p, 1, 0);
49    } while (r == -1 && (errno == EINTR || errno == EAGAIN));
50
51    /* If there was an error, r will be < 0.  Return that.  If there was
52     * nothing to process, r == 0.  Return that.
53     */
54    if (r <= 0)
55        return r;
56
57    /* Try to handle the event.  If there was an error, return it. */
58    r = drmHandleEvent(ms->fd, &ms->event_context);
59    if (r < 0)
60        return r;
61
62    /* Otherwise return 1 to indicate that we handled an event. */
63    return 1;
64}
65
66#ifdef GLAMOR_HAS_GBM
67
68/*
69 * Event data for an in progress flip.
70 * This contains a pointer to the vblank event,
71 * and information about the flip in progress.
72 * a reference to this is stored in the per-crtc
73 * flips.
74 */
75struct ms_flipdata {
76    ScreenPtr screen;
77    void *event;
78    ms_pageflip_handler_proc event_handler;
79    ms_pageflip_abort_proc abort_handler;
80    /* number of CRTC events referencing this */
81    int flip_count;
82    uint64_t fe_msc;
83    uint64_t fe_usec;
84    uint32_t old_fb_id;
85};
86
87/*
88 * Per crtc pageflipping information,
89 * These are submitted to the queuing code
90 * one of them per crtc per flip.
91 */
92struct ms_crtc_pageflip {
93    Bool on_reference_crtc;
94    /* reference to the ms_flipdata */
95    struct ms_flipdata *flipdata;
96};
97
98/**
99 * Free an ms_crtc_pageflip.
100 *
101 * Drops the reference count on the flipdata.
102 */
103static void
104ms_pageflip_free(struct ms_crtc_pageflip *flip)
105{
106    struct ms_flipdata *flipdata = flip->flipdata;
107
108    free(flip);
109    if (--flipdata->flip_count > 0)
110        return;
111    free(flipdata);
112}
113
114/**
115 * Callback for the DRM event queue when a single flip has completed
116 *
117 * Once the flip has been completed on all pipes, notify the
118 * extension code telling it when that happened
119 */
120static void
121ms_pageflip_handler(uint64_t msc, uint64_t ust, void *data)
122{
123    struct ms_crtc_pageflip *flip = data;
124    struct ms_flipdata *flipdata = flip->flipdata;
125    ScreenPtr screen = flipdata->screen;
126    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
127    modesettingPtr ms = modesettingPTR(scrn);
128
129    if (flip->on_reference_crtc) {
130        flipdata->fe_msc = msc;
131        flipdata->fe_usec = ust;
132    }
133
134    if (flipdata->flip_count == 1) {
135        flipdata->event_handler(ms, flipdata->fe_msc,
136                                flipdata->fe_usec,
137                                flipdata->event);
138
139        drmModeRmFB(ms->fd, flipdata->old_fb_id);
140    }
141    ms_pageflip_free(flip);
142}
143
144/*
145 * Callback for the DRM queue abort code.  A flip has been aborted.
146 */
147static void
148ms_pageflip_abort(void *data)
149{
150    struct ms_crtc_pageflip *flip = data;
151    struct ms_flipdata *flipdata = flip->flipdata;
152    ScreenPtr screen = flipdata->screen;
153    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
154    modesettingPtr ms = modesettingPTR(scrn);
155
156    if (flipdata->flip_count == 1)
157        flipdata->abort_handler(ms, flipdata->event);
158
159    ms_pageflip_free(flip);
160}
161
162static Bool
163do_queue_flip_on_crtc(modesettingPtr ms, xf86CrtcPtr crtc,
164                      uint32_t flags, uint32_t seq)
165{
166    return drmmode_crtc_flip(crtc, ms->drmmode.fb_id, flags,
167                             (void *) (uintptr_t) seq);
168}
169
170enum queue_flip_status {
171    QUEUE_FLIP_SUCCESS,
172    QUEUE_FLIP_ALLOC_FAILED,
173    QUEUE_FLIP_QUEUE_ALLOC_FAILED,
174    QUEUE_FLIP_DRM_FLUSH_FAILED,
175};
176
177static int
178queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
179                   struct ms_flipdata *flipdata,
180                   int ref_crtc_vblank_pipe, uint32_t flags)
181{
182    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
183    modesettingPtr ms = modesettingPTR(scrn);
184    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
185    struct ms_crtc_pageflip *flip;
186    uint32_t seq;
187
188    flip = calloc(1, sizeof(struct ms_crtc_pageflip));
189    if (flip == NULL) {
190        return QUEUE_FLIP_ALLOC_FAILED;
191    }
192
193    /* Only the reference crtc will finally deliver its page flip
194     * completion event. All other crtc's events will be discarded.
195     */
196    flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
197    flip->flipdata = flipdata;
198
199    seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
200    if (!seq) {
201        free(flip);
202        return QUEUE_FLIP_QUEUE_ALLOC_FAILED;
203    }
204
205    /* take a reference on flipdata for use in flip */
206    flipdata->flip_count++;
207
208    while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
209        /* We may have failed because the event queue was full.  Flush it
210         * and retry.  If there was nothing to flush, then we failed for
211         * some other reason and should just return an error.
212         */
213        if (ms_flush_drm_events(screen) <= 0) {
214            /* Aborting will also decrement flip_count and free(flip). */
215            ms_drm_abort_seq(scrn, seq);
216            return QUEUE_FLIP_DRM_FLUSH_FAILED;
217        }
218
219        /* We flushed some events, so try again. */
220        xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
221    }
222
223    /* The page flip succeeded. */
224    return QUEUE_FLIP_SUCCESS;
225}
226
227
228#define MS_ASYNC_FLIP_LOG_ENABLE_LOGS_INTERVAL_MS 10000
229#define MS_ASYNC_FLIP_LOG_FREQUENT_LOGS_INTERVAL_MS 1000
230#define MS_ASYNC_FLIP_FREQUENT_LOG_COUNT 10
231
232static void
233ms_print_pageflip_error(int screen_index, const char *log_prefix,
234                        int crtc_index, int flags, int err)
235{
236    /* In certain circumstances we will have a lot of flip errors without a
237     * reasonable way to prevent them. In such case we reduce the number of
238     * logged messages to at least not fill the error logs.
239     *
240     * The details are as follows:
241     *
242     * At least on i915 hardware support for async page flip support depends
243     * on the used modifiers which themselves can change dynamically for a
244     * screen. This results in the following problems:
245     *
246     *  - We can't know about whether a particular CRTC will be able to do an
247     *    async flip without hardcoding the same logic as the kernel as there's
248     *    no interface to query this information.
249     *
250     *  - There is no way to give this information to an application, because
251     *    the protocol of the present extension does not specify anything about
252     *    changing of the capabilities on runtime or the need to re-query them.
253     *
254     * Even if the above was solved, the only benefit would be avoiding a
255     * roundtrip to the kernel and reduced amount of error logs. The former
256     * does not seem to be a good enough benefit compared to the amount of work
257     * that would need to be done. The latter is solved below. */
258
259    static CARD32 error_last_time_ms;
260    static int frequent_logs;
261    static Bool logs_disabled;
262
263    if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
264        CARD32 curr_time_ms = GetTimeInMillis();
265        int clocks_since_last_log = curr_time_ms - error_last_time_ms;
266
267        if (clocks_since_last_log >
268                MS_ASYNC_FLIP_LOG_ENABLE_LOGS_INTERVAL_MS) {
269            frequent_logs = 0;
270            logs_disabled = FALSE;
271        }
272        if (!logs_disabled) {
273            if (clocks_since_last_log <
274                    MS_ASYNC_FLIP_LOG_FREQUENT_LOGS_INTERVAL_MS) {
275                frequent_logs++;
276            }
277
278            if (frequent_logs > MS_ASYNC_FLIP_FREQUENT_LOG_COUNT) {
279                xf86DrvMsg(screen_index, X_WARNING,
280                           "%s: detected too frequent flip errors, disabling "
281                           "logs until frequency is reduced\n", log_prefix);
282                logs_disabled = TRUE;
283            } else {
284                xf86DrvMsg(screen_index, X_WARNING,
285                           "%s: queue async flip during flip on CRTC %d failed: %s\n",
286                           log_prefix, crtc_index, strerror(err));
287            }
288        }
289        error_last_time_ms = curr_time_ms;
290    } else {
291        xf86DrvMsg(screen_index, X_WARNING,
292                   "%s: queue flip during flip on CRTC %d failed: %s\n",
293                   log_prefix, crtc_index, strerror(err));
294    }
295}
296
297
298Bool
299ms_do_pageflip(ScreenPtr screen,
300               PixmapPtr new_front,
301               void *event,
302               int ref_crtc_vblank_pipe,
303               Bool async,
304               ms_pageflip_handler_proc pageflip_handler,
305               ms_pageflip_abort_proc pageflip_abort,
306               const char *log_prefix)
307{
308#ifndef GLAMOR_HAS_GBM
309    return FALSE;
310#else
311    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
312    modesettingPtr ms = modesettingPTR(scrn);
313    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
314    drmmode_bo new_front_bo;
315    uint32_t flags;
316    int i;
317    struct ms_flipdata *flipdata;
318    ms->glamor.block_handler(screen);
319
320    new_front_bo.gbm = ms->glamor.gbm_bo_from_pixmap(screen, new_front);
321    new_front_bo.dumb = NULL;
322
323    if (!new_front_bo.gbm) {
324        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
325                   "%s: Failed to get GBM BO for flip to new front.\n",
326                   log_prefix);
327        return FALSE;
328    }
329
330    flipdata = calloc(1, sizeof(struct ms_flipdata));
331    if (!flipdata) {
332        drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
333        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
334                   "%s: Failed to allocate flipdata.\n", log_prefix);
335        return FALSE;
336    }
337
338    flipdata->event = event;
339    flipdata->screen = screen;
340    flipdata->event_handler = pageflip_handler;
341    flipdata->abort_handler = pageflip_abort;
342
343    /*
344     * Take a local reference on flipdata.
345     * if the first flip fails, the sequence abort
346     * code will free the crtc flip data, and drop
347     * its reference which would cause this to be
348     * freed when we still required it.
349     */
350    flipdata->flip_count++;
351
352    /* Create a new handle for the back buffer */
353    flipdata->old_fb_id = ms->drmmode.fb_id;
354
355    new_front_bo.width = new_front->drawable.width;
356    new_front_bo.height = new_front->drawable.height;
357    if (drmmode_bo_import(&ms->drmmode, &new_front_bo,
358                          &ms->drmmode.fb_id)) {
359        if (!ms->drmmode.flip_bo_import_failed) {
360            xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: Import BO failed: %s\n",
361                       log_prefix, strerror(errno));
362            ms->drmmode.flip_bo_import_failed = TRUE;
363        }
364        goto error_out;
365    } else {
366        if (ms->drmmode.flip_bo_import_failed &&
367            new_front != screen->GetScreenPixmap(screen))
368            ms->drmmode.flip_bo_import_failed = FALSE;
369    }
370
371    /* Queue flips on all enabled CRTCs.
372     *
373     * Note that if/when we get per-CRTC buffers, we'll have to update this.
374     * Right now it assumes a single shared fb across all CRTCs, with the
375     * kernel fixing up the offset of each CRTC as necessary.
376     *
377     * Also, flips queued on disabled or incorrectly configured displays
378     * may never complete; this is a configuration error.
379     */
380    for (i = 0; i < config->num_crtc; i++) {
381        enum queue_flip_status flip_status;
382        xf86CrtcPtr crtc = config->crtc[i];
383        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
384
385        if (!xf86_crtc_on(crtc))
386            continue;
387
388        flags = DRM_MODE_PAGE_FLIP_EVENT;
389        if (ms->drmmode.can_async_flip && async)
390            flags |= DRM_MODE_PAGE_FLIP_ASYNC;
391
392        /*
393         * If this is not the reference crtc used for flip timing and flip event
394         * delivery and timestamping, ie. not the one whose presentation timing
395         * we do really care about, and async flips are possible, and requested
396         * by an xorg.conf option, then we flip this "secondary" crtc without
397         * sync to vblank. This may cause tearing on such "secondary" outputs,
398         * but it will prevent throttling of multi-display flips to the refresh
399         * cycle of any of the secondary crtcs, avoiding periodic slowdowns and
400         * judder caused by unsynchronized outputs. This is especially useful for
401         * outputs in a "clone-mode" or "mirror-mode" configuration.
402         */
403        if (ms->drmmode.can_async_flip && ms->drmmode.async_flip_secondaries &&
404            (drmmode_crtc->vblank_pipe != ref_crtc_vblank_pipe) &&
405            (ref_crtc_vblank_pipe >= 0))
406            flags |= DRM_MODE_PAGE_FLIP_ASYNC;
407
408        flip_status = queue_flip_on_crtc(screen, crtc, flipdata,
409                                         ref_crtc_vblank_pipe,
410                                         flags);
411
412        switch (flip_status) {
413            case QUEUE_FLIP_ALLOC_FAILED:
414                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
415                           "%s: carrier alloc for queue flip on CRTC %d failed.\n",
416                           log_prefix, i);
417                goto error_undo;
418            case QUEUE_FLIP_QUEUE_ALLOC_FAILED:
419                xf86DrvMsg(scrn->scrnIndex, X_WARNING,
420                           "%s: entry alloc for queue flip on CRTC %d failed.\n",
421                           log_prefix, i);
422                goto error_undo;
423            case QUEUE_FLIP_DRM_FLUSH_FAILED:
424                ms_print_pageflip_error(scrn->scrnIndex, log_prefix, i, flags, errno);
425                goto error_undo;
426            case QUEUE_FLIP_SUCCESS:
427                break;
428        }
429    }
430
431    drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
432
433    /*
434     * Do we have more than our local reference,
435     * if so and no errors, then drop our local
436     * reference and return now.
437     */
438    if (flipdata->flip_count > 1) {
439        flipdata->flip_count--;
440        return TRUE;
441    }
442
443error_undo:
444
445    /*
446     * Have we just got the local reference?
447     * free the framebuffer if so since nobody successfully
448     * submitted anything
449     */
450    if (flipdata->flip_count == 1) {
451        drmModeRmFB(ms->fd, ms->drmmode.fb_id);
452        ms->drmmode.fb_id = flipdata->old_fb_id;
453    }
454
455error_out:
456    drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
457    /* if only the local reference - free the structure,
458     * else drop the local reference and return */
459    if (flipdata->flip_count == 1)
460        free(flipdata);
461    else
462        flipdata->flip_count--;
463
464    return FALSE;
465#endif /* GLAMOR_HAS_GBM */
466}
467
468#endif
469