pageflip.c revision a035e2b2
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 infomation,
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
170static Bool
171queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc,
172                   struct ms_flipdata *flipdata,
173                   int ref_crtc_vblank_pipe, uint32_t flags)
174{
175    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
176    modesettingPtr ms = modesettingPTR(scrn);
177    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
178    struct ms_crtc_pageflip *flip;
179    uint32_t seq;
180    int err;
181
182    flip = calloc(1, sizeof(struct ms_crtc_pageflip));
183    if (flip == NULL) {
184        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
185                   "flip queue: carrier alloc failed.\n");
186        return FALSE;
187    }
188
189    /* Only the reference crtc will finally deliver its page flip
190     * completion event. All other crtc's events will be discarded.
191     */
192    flip->on_reference_crtc = (drmmode_crtc->vblank_pipe == ref_crtc_vblank_pipe);
193    flip->flipdata = flipdata;
194
195    seq = ms_drm_queue_alloc(crtc, flip, ms_pageflip_handler, ms_pageflip_abort);
196    if (!seq) {
197        free(flip);
198        return FALSE;
199    }
200
201    /* take a reference on flipdata for use in flip */
202    flipdata->flip_count++;
203
204    while (do_queue_flip_on_crtc(ms, crtc, flags, seq)) {
205        err = errno;
206        /* We may have failed because the event queue was full.  Flush it
207         * and retry.  If there was nothing to flush, then we failed for
208         * some other reason and should just return an error.
209         */
210        if (ms_flush_drm_events(screen) <= 0) {
211            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
212                       "flip queue failed: %s\n", strerror(err));
213            /* Aborting will also decrement flip_count and free(flip). */
214            ms_drm_abort_seq(scrn, seq);
215            return FALSE;
216        }
217
218        /* We flushed some events, so try again. */
219        xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue retry\n");
220    }
221
222    /* The page flip succeded. */
223    return TRUE;
224}
225
226
227Bool
228ms_do_pageflip(ScreenPtr screen,
229               PixmapPtr new_front,
230               void *event,
231               int ref_crtc_vblank_pipe,
232               Bool async,
233               ms_pageflip_handler_proc pageflip_handler,
234               ms_pageflip_abort_proc pageflip_abort)
235{
236#ifndef GLAMOR_HAS_GBM
237    return FALSE;
238#else
239    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
240    modesettingPtr ms = modesettingPTR(scrn);
241    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
242    drmmode_bo new_front_bo;
243    uint32_t flags;
244    int i;
245    struct ms_flipdata *flipdata;
246    glamor_block_handler(screen);
247
248    new_front_bo.gbm = glamor_gbm_bo_from_pixmap(screen, new_front);
249    new_front_bo.dumb = NULL;
250
251    if (!new_front_bo.gbm) {
252        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
253                   "Failed to get GBM bo for flip to new front.\n");
254        return FALSE;
255    }
256
257    flipdata = calloc(1, sizeof(struct ms_flipdata));
258    if (!flipdata) {
259        drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
260        xf86DrvMsg(scrn->scrnIndex, X_ERROR,
261                   "Failed to allocate flipdata.\n");
262        return FALSE;
263    }
264
265    flipdata->event = event;
266    flipdata->screen = screen;
267    flipdata->event_handler = pageflip_handler;
268    flipdata->abort_handler = pageflip_abort;
269
270    /*
271     * Take a local reference on flipdata.
272     * if the first flip fails, the sequence abort
273     * code will free the crtc flip data, and drop
274     * it's reference which would cause this to be
275     * freed when we still required it.
276     */
277    flipdata->flip_count++;
278
279    /* Create a new handle for the back buffer */
280    flipdata->old_fb_id = ms->drmmode.fb_id;
281
282    new_front_bo.width = new_front->drawable.width;
283    new_front_bo.height = new_front->drawable.height;
284    if (drmmode_bo_import(&ms->drmmode, &new_front_bo,
285                          &ms->drmmode.fb_id))
286        goto error_out;
287
288    flags = DRM_MODE_PAGE_FLIP_EVENT;
289    if (async)
290        flags |= DRM_MODE_PAGE_FLIP_ASYNC;
291
292    /* Queue flips on all enabled CRTCs.
293     *
294     * Note that if/when we get per-CRTC buffers, we'll have to update this.
295     * Right now it assumes a single shared fb across all CRTCs, with the
296     * kernel fixing up the offset of each CRTC as necessary.
297     *
298     * Also, flips queued on disabled or incorrectly configured displays
299     * may never complete; this is a configuration error.
300     */
301    for (i = 0; i < config->num_crtc; i++) {
302        xf86CrtcPtr crtc = config->crtc[i];
303
304        if (!xf86_crtc_on(crtc))
305            continue;
306
307        if (!queue_flip_on_crtc(screen, crtc, flipdata,
308                                ref_crtc_vblank_pipe,
309                                flags)) {
310            goto error_undo;
311        }
312    }
313
314    drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
315
316    /*
317     * Do we have more than our local reference,
318     * if so and no errors, then drop our local
319     * reference and return now.
320     */
321    if (flipdata->flip_count > 1) {
322        flipdata->flip_count--;
323        return TRUE;
324    }
325
326error_undo:
327
328    /*
329     * Have we just got the local reference?
330     * free the framebuffer if so since nobody successfully
331     * submitted anything
332     */
333    if (flipdata->flip_count == 1) {
334        drmModeRmFB(ms->fd, ms->drmmode.fb_id);
335        ms->drmmode.fb_id = flipdata->old_fb_id;
336    }
337
338error_out:
339    xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n",
340               strerror(errno));
341    drmmode_bo_destroy(&ms->drmmode, &new_front_bo);
342    /* if only the local reference - free the structure,
343     * else drop the local reference and return */
344    if (flipdata->flip_count == 1)
345        free(flipdata);
346    else
347        flipdata->flip_count--;
348
349    return FALSE;
350#endif /* GLAMOR_HAS_GBM */
351}
352
353#endif
354