present.c revision 7e31ba66
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 <assert.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <unistd.h>
31#include <stdio.h>
32#include <stdint.h>
33#include <string.h>
34#include <sys/ioctl.h>
35#include <sys/time.h>
36#include <sys/types.h>
37#include <time.h>
38
39#include <xf86.h>
40#include <xf86Crtc.h>
41#include <xf86drm.h>
42#include <xf86str.h>
43#include <present.h>
44
45#include "driver.h"
46#include "drmmode_display.h"
47
48#if 0
49#define DebugPresent(x) ErrorF x
50#else
51#define DebugPresent(x)
52#endif
53
54struct ms_present_vblank_event {
55    uint64_t        event_id;
56    Bool            unflip;
57};
58
59static RRCrtcPtr
60ms_present_get_crtc(WindowPtr window)
61{
62    xf86CrtcPtr xf86_crtc = ms_dri2_crtc_covering_drawable(&window->drawable);
63    return xf86_crtc ? xf86_crtc->randr_crtc : NULL;
64}
65
66static int
67ms_present_get_ust_msc(RRCrtcPtr crtc, uint64_t *ust, uint64_t *msc)
68{
69    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
70
71    return ms_get_crtc_ust_msc(xf86_crtc, ust, msc);
72}
73
74/*
75 * Called when the queued vblank event has occurred
76 */
77static void
78ms_present_vblank_handler(uint64_t msc, uint64_t usec, void *data)
79{
80    struct ms_present_vblank_event *event = data;
81
82    DebugPresent(("\t\tmh %lld msc %llu\n",
83                 (long long) event->event_id, (long long) msc));
84
85    present_event_notify(event->event_id, usec, msc);
86    free(event);
87}
88
89/*
90 * Called when the queued vblank is aborted
91 */
92static void
93ms_present_vblank_abort(void *data)
94{
95    struct ms_present_vblank_event *event = data;
96
97    DebugPresent(("\t\tma %lld\n", (long long) event->event_id));
98
99    free(event);
100}
101
102/*
103 * Queue an event to report back to the Present extension when the specified
104 * MSC has past
105 */
106static int
107ms_present_queue_vblank(RRCrtcPtr crtc,
108                        uint64_t event_id,
109                        uint64_t msc)
110{
111    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
112    struct ms_present_vblank_event *event;
113    uint32_t seq;
114
115    event = calloc(sizeof(struct ms_present_vblank_event), 1);
116    if (!event)
117        return BadAlloc;
118    event->event_id = event_id;
119    seq = ms_drm_queue_alloc(xf86_crtc, event,
120                             ms_present_vblank_handler,
121                             ms_present_vblank_abort);
122    if (!seq) {
123        free(event);
124        return BadAlloc;
125    }
126
127    if (!ms_queue_vblank(xf86_crtc, MS_QUEUE_ABSOLUTE, msc, NULL, seq))
128        return BadAlloc;
129
130    DebugPresent(("\t\tmq %lld seq %u msc %llu (hw msc %u)\n",
131                 (long long) event_id, seq, (long long) msc,
132                 vbl.request.sequence));
133    return Success;
134}
135
136static Bool
137ms_present_event_match(void *data, void *match_data)
138{
139    struct ms_present_vblank_event *event = data;
140    uint64_t *match = match_data;
141
142    return *match == event->event_id;
143}
144
145/*
146 * Remove a pending vblank event from the DRM queue so that it is not reported
147 * to the extension
148 */
149static void
150ms_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
151{
152    ScreenPtr screen = crtc->pScreen;
153    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
154
155    ms_drm_abort(scrn, ms_present_event_match, &event_id);
156}
157
158/*
159 * Flush our batch buffer when requested by the Present extension.
160 */
161static void
162ms_present_flush(WindowPtr window)
163{
164#ifdef GLAMOR_HAS_GBM
165    ScreenPtr screen = window->drawable.pScreen;
166    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
167    modesettingPtr ms = modesettingPTR(scrn);
168
169    if (ms->drmmode.glamor)
170        glamor_block_handler(screen);
171#endif
172}
173
174#ifdef GLAMOR_HAS_GBM
175
176/**
177 * Callback for the DRM event queue when a flip has completed on all pipes
178 *
179 * Notify the extension code
180 */
181static void
182ms_present_flip_handler(modesettingPtr ms, uint64_t msc,
183                        uint64_t ust, void *data)
184{
185    struct ms_present_vblank_event *event = data;
186
187    DebugPresent(("\t\tms:fc %lld msc %llu ust %llu\n",
188                  (long long) event->event_id,
189                  (long long) msc, (long long) ust));
190
191    if (event->unflip)
192        ms->drmmode.present_flipping = FALSE;
193
194    ms_present_vblank_handler(msc, ust, event);
195}
196
197/*
198 * Callback for the DRM queue abort code.  A flip has been aborted.
199 */
200static void
201ms_present_flip_abort(modesettingPtr ms, void *data)
202{
203    struct ms_present_vblank_event *event = data;
204
205    DebugPresent(("\t\tms:fa %lld\n", (long long) event->event_id));
206
207    free(event);
208}
209
210/*
211 * Test to see if page flipping is possible on the target crtc
212 */
213static Bool
214ms_present_check_flip(RRCrtcPtr crtc,
215                      WindowPtr window,
216                      PixmapPtr pixmap,
217                      Bool sync_flip,
218                      PresentFlipReason *reason)
219{
220    ScreenPtr screen = window->drawable.pScreen;
221    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
222    modesettingPtr ms = modesettingPTR(scrn);
223    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
224    int num_crtcs_on = 0;
225    int i;
226    struct gbm_bo *gbm;
227
228    if (!ms->drmmode.pageflip)
229        return FALSE;
230
231    if (ms->drmmode.dri2_flipping)
232        return FALSE;
233
234    if (!scrn->vtSema)
235        return FALSE;
236
237    for (i = 0; i < config->num_crtc; i++) {
238        drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private;
239
240        /* Don't do pageflipping if CRTCs are rotated. */
241        if (drmmode_crtc->rotate_bo.gbm)
242            return FALSE;
243
244        if (ms_crtc_on(config->crtc[i]))
245            num_crtcs_on++;
246    }
247
248    /* We can't do pageflipping if all the CRTCs are off. */
249    if (num_crtcs_on == 0)
250        return FALSE;
251
252    /* Check stride, can't change that on flip */
253    if (!ms->atomic_modeset &&
254        pixmap->devKind != drmmode_bo_get_pitch(&ms->drmmode.front_bo))
255        return FALSE;
256
257#ifdef GBM_BO_WITH_MODIFIERS
258    /* Check if buffer format/modifier is supported by all active CRTCs */
259    gbm = glamor_gbm_bo_from_pixmap(screen, pixmap);
260    if (gbm) {
261        uint32_t format;
262        uint64_t modifier;
263
264        format = gbm_bo_get_format(gbm);
265        modifier = gbm_bo_get_modifier(gbm);
266        gbm_bo_destroy(gbm);
267
268        if (!drmmode_is_format_supported(scrn, format, modifier)) {
269            if (reason)
270                *reason = PRESENT_FLIP_REASON_BUFFER_FORMAT;
271            return FALSE;
272        }
273    }
274#endif
275
276    /* Make sure there's a bo we can get to */
277    /* XXX: actually do this.  also...is it sufficient?
278     * if (!glamor_get_pixmap_private(pixmap))
279     *     return FALSE;
280     */
281
282    return TRUE;
283}
284
285/*
286 * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
287 * then wait for vblank. Otherwise, flip immediately
288 */
289static Bool
290ms_present_flip(RRCrtcPtr crtc,
291                uint64_t event_id,
292                uint64_t target_msc,
293                PixmapPtr pixmap,
294                Bool sync_flip)
295{
296    ScreenPtr screen = crtc->pScreen;
297    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
298    modesettingPtr ms = modesettingPTR(scrn);
299    xf86CrtcPtr xf86_crtc = crtc->devPrivate;
300    drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
301    Bool ret;
302    struct ms_present_vblank_event *event;
303
304    if (!ms_present_check_flip(crtc, screen->root, pixmap, sync_flip, NULL))
305        return FALSE;
306
307    event = calloc(1, sizeof(struct ms_present_vblank_event));
308    if (!event)
309        return FALSE;
310
311    DebugPresent(("\t\tms:pf %lld msc %llu\n",
312                  (long long) event_id, (long long) target_msc));
313
314    event->event_id = event_id;
315    event->unflip = FALSE;
316
317    ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip,
318                         ms_present_flip_handler, ms_present_flip_abort);
319    if (!ret)
320        xf86DrvMsg(scrn->scrnIndex, X_ERROR, "present flip failed\n");
321    else
322        ms->drmmode.present_flipping = TRUE;
323
324    return ret;
325}
326
327/*
328 * Queue a flip back to the normal frame buffer
329 */
330static void
331ms_present_unflip(ScreenPtr screen, uint64_t event_id)
332{
333    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
334    modesettingPtr ms = modesettingPTR(scrn);
335    PixmapPtr pixmap = screen->GetScreenPixmap(screen);
336    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
337    int i;
338    struct ms_present_vblank_event *event;
339
340    event = calloc(1, sizeof(struct ms_present_vblank_event));
341    if (!event)
342        return;
343
344    event->event_id = event_id;
345    event->unflip = TRUE;
346
347    if (ms_present_check_flip(NULL, screen->root, pixmap, TRUE, NULL) &&
348        ms_do_pageflip(screen, pixmap, event, -1, FALSE,
349                       ms_present_flip_handler, ms_present_flip_abort)) {
350        return;
351    }
352
353    for (i = 0; i < config->num_crtc; i++) {
354        xf86CrtcPtr crtc = config->crtc[i];
355	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
356
357	if (!crtc->enabled)
358	    continue;
359
360	/* info->drmmode.fb_id still points to the FB for the last flipped BO.
361	 * Clear it, drmmode_set_mode_major will re-create it
362	 */
363	if (drmmode_crtc->drmmode->fb_id) {
364		drmModeRmFB(drmmode_crtc->drmmode->fd,
365			    drmmode_crtc->drmmode->fb_id);
366		drmmode_crtc->drmmode->fb_id = 0;
367	}
368
369	if (drmmode_crtc->dpms_mode == DPMSModeOn)
370	    crtc->funcs->set_mode_major(crtc, &crtc->mode, crtc->rotation,
371					crtc->x, crtc->y);
372	else
373	    drmmode_crtc->need_modeset = TRUE;
374    }
375
376    present_event_notify(event_id, 0, 0);
377    ms->drmmode.present_flipping = FALSE;
378}
379#endif
380
381static present_screen_info_rec ms_present_screen_info = {
382    .version = PRESENT_SCREEN_INFO_VERSION,
383
384    .get_crtc = ms_present_get_crtc,
385    .get_ust_msc = ms_present_get_ust_msc,
386    .queue_vblank = ms_present_queue_vblank,
387    .abort_vblank = ms_present_abort_vblank,
388    .flush = ms_present_flush,
389
390    .capabilities = PresentCapabilityNone,
391#ifdef GLAMOR_HAS_GBM
392    .check_flip = NULL,
393    .check_flip2 = ms_present_check_flip,
394    .flip = ms_present_flip,
395    .unflip = ms_present_unflip,
396#endif
397};
398
399Bool
400ms_present_screen_init(ScreenPtr screen)
401{
402    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
403    modesettingPtr ms = modesettingPTR(scrn);
404    uint64_t value;
405    int ret;
406
407    ret = drmGetCap(ms->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
408    if (ret == 0 && value == 1)
409        ms_present_screen_info.capabilities |= PresentCapabilityAsync;
410
411    return present_screen_init(screen, &ms_present_screen_info);
412}
413