intel_present.c revision 13496ba1
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_CONFIG_H
24#include "config.h"
25#endif
26
27#include <stdio.h>
28#include <string.h>
29#include <assert.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/ioctl.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include <sys/time.h>
36#include <time.h>
37#include <errno.h>
38
39#include "xorg-server.h"
40#include "xf86.h"
41#include "xf86_OSproc.h"
42
43#include "xf86Pci.h"
44#include "xf86drm.h"
45
46#include "windowstr.h"
47#include "shadow.h"
48#include "fb.h"
49
50#include "intel.h"
51#include "i830_reg.h"
52
53#include "i915_drm.h"
54
55#include "present.h"
56
57struct intel_present_vblank_event {
58	uint64_t        event_id;
59};
60
61static uint32_t pipe_select(int pipe)
62{
63	if (pipe > 1)
64		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
65	else if (pipe > 0)
66		return DRM_VBLANK_SECONDARY;
67	else
68		return 0;
69}
70
71static RRCrtcPtr
72intel_present_get_crtc(WindowPtr window)
73{
74	ScreenPtr screen = window->drawable.pScreen;
75	ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
76	BoxRec box, crtcbox;
77	xf86CrtcPtr crtc;
78	RRCrtcPtr randr_crtc = NULL;
79
80	box.x1 = window->drawable.x;
81	box.y1 = window->drawable.y;
82	box.x2 = box.x1 + window->drawable.width;
83	box.y2 = box.y1 + window->drawable.height;
84
85	crtc = intel_covering_crtc(pScrn, &box, NULL, &crtcbox);
86
87	/* Make sure the CRTC is valid and this is the real front buffer */
88	if (crtc != NULL && !crtc->rotatedData)
89		randr_crtc = crtc->randr_crtc;
90
91	return randr_crtc;
92}
93
94static int
95intel_present_crtc_pipe(ScreenPtr screen, RRCrtcPtr randr_crtc)
96{
97	xf86CrtcPtr crtc;
98
99	if (randr_crtc == NULL)
100		return 0;
101
102	crtc = randr_crtc->devPrivate;
103	return intel_crtc_to_pipe(crtc);
104}
105
106static int
107intel_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
108{
109	xf86CrtcPtr             xf86_crtc = crtc->devPrivate;
110	ScreenPtr               screen = crtc->pScreen;
111	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
112
113	return intel_get_crtc_msc_ust(scrn, xf86_crtc, msc, ust);
114}
115
116/*
117 * Flush the DRM event queue when full; this
118 * makes space for new requests
119 */
120static Bool
121intel_present_flush_drm_events(ScreenPtr screen)
122{
123	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
124	intel_screen_private    *intel = intel_get_screen_private(scrn);
125
126	return intel_mode_read_drm_events(intel) >= 0;
127}
128
129/*
130 * Called when the queued vblank event has occurred
131 */
132static void
133intel_present_vblank_handler(ScrnInfoPtr scrn, xf86CrtcPtr crtc, uint64_t msc, uint64_t usec, void *data)
134{
135	struct intel_present_vblank_event       *event = data;
136
137	present_event_notify(event->event_id, usec, msc);
138	free(event);
139}
140
141/*
142 * Called when the queued vblank is aborted
143 */
144static void
145intel_present_vblank_abort(ScrnInfoPtr scrn, xf86CrtcPtr crtc, void *data)
146{
147	struct intel_present_vblank_event       *event = data;
148
149	free(event);
150}
151
152/*
153 * Queue an event to report back to the Present extension when the specified
154 * MSC has past
155 */
156static int
157intel_present_queue_vblank(RRCrtcPtr                    crtc,
158                           uint64_t                     event_id,
159                           uint64_t                     msc)
160{
161	xf86CrtcPtr                             xf86_crtc = crtc->devPrivate;
162	ScreenPtr                               screen = crtc->pScreen;
163	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
164	intel_screen_private                    *intel = intel_get_screen_private(scrn);
165	int                                     pipe = intel_present_crtc_pipe(screen, crtc);
166	struct intel_present_vblank_event       *event;
167	drmVBlank                               vbl;
168	int                                     ret;
169	uint32_t                                seq;
170
171	event = calloc(sizeof(struct intel_present_vblank_event), 1);
172	if (!event)
173		return BadAlloc;
174	event->event_id = event_id;
175	seq = intel_drm_queue_alloc(scrn, xf86_crtc, event,
176				    intel_present_vblank_handler,
177				    intel_present_vblank_abort);
178	if (!seq) {
179		free(event);
180		return BadAlloc;
181	}
182
183	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT | pipe_select(pipe);
184	vbl.request.sequence = intel_crtc_msc_to_sequence(scrn, xf86_crtc, msc);
185	vbl.request.signal = seq;
186	for (;;) {
187		ret = drmWaitVBlank(intel->drmSubFD, &vbl);
188		if (!ret)
189			break;
190		if (errno != EBUSY || !intel_present_flush_drm_events(screen))
191			return BadAlloc;
192	}
193	DebugPresent(("\t\tiq %lld seq %u msc %llu (hw msc %u)\n",
194                      (long long) event_id, seq, (long long) msc, vbl.request.sequence));
195	return Success;
196}
197
198static Bool
199intel_present_event_match(void *data, void *match_data)
200{
201	struct intel_present_vblank_event       *event = data;
202	uint64_t                                *match = match_data;
203
204	return *match == event->event_id;
205}
206
207/*
208 * Remove a pending vblank event from the DRM queue so that it is not reported
209 * to the extension
210 */
211static void
212intel_present_abort_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
213{
214	ScreenPtr       screen = crtc->pScreen;
215	ScrnInfoPtr     scrn = xf86ScreenToScrn(screen);
216
217	intel_drm_abort(scrn, intel_present_event_match, &event_id);
218}
219
220/*
221 * Flush our batch buffer when requested by the Present extension.
222 */
223static void
224intel_present_flush(WindowPtr window)
225{
226	ScreenPtr                               screen = window->drawable.pScreen;
227	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
228	intel_screen_private                    *intel = intel_get_screen_private(scrn);
229
230	if (intel->flush_rendering)
231		intel->flush_rendering(intel);
232}
233
234/*
235 * Test to see if page flipping is possible on the target crtc
236 */
237static Bool
238intel_present_check_flip(RRCrtcPtr              crtc,
239                         WindowPtr              window,
240                         PixmapPtr              pixmap,
241                         Bool                   sync_flip)
242{
243	ScreenPtr               screen = window->drawable.pScreen;
244	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
245	intel_screen_private    *intel = intel_get_screen_private(scrn);
246        dri_bo                  *bo;
247
248	if (!scrn->vtSema)
249		return FALSE;
250
251	if (intel->shadow_present)
252		return FALSE;
253
254	if (!intel->use_pageflipping)
255		return FALSE;
256
257	if (crtc && !intel_crtc_on(crtc->devPrivate))
258		return FALSE;
259
260        /* Check stride, can't change that on flip */
261        if (pixmap->devKind != intel->front_pitch)
262                return FALSE;
263
264        /* Make sure there's a bo we can get to */
265        bo = intel_get_pixmap_bo(pixmap);
266        if (!bo)
267                return FALSE;
268
269	return TRUE;
270}
271
272/*
273 * Once the flip has been completed on all pipes, notify the
274 * extension code telling it when that happened
275 */
276static void
277intel_present_flip_event(uint64_t msc, uint64_t ust, void *pageflip_data)
278{
279	struct intel_present_vblank_event *event = pageflip_data;
280
281	present_event_notify(event->event_id, ust, msc);
282	free(event);
283}
284
285/*
286 * The flip has been aborted, free the structure
287 */
288static void
289intel_present_flip_abort(void *pageflip_data)
290{
291	struct intel_present_vblank_event *event = pageflip_data;
292
293	free(event);
294}
295
296/*
297 * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
298 * then wait for vblank. Otherwise, flip immediately
299 */
300static Bool
301intel_present_flip(RRCrtcPtr                    crtc,
302                   uint64_t                     event_id,
303                   uint64_t                     target_msc,
304                   PixmapPtr                    pixmap,
305                   Bool                         sync_flip)
306{
307	ScreenPtr                               screen = crtc->pScreen;
308	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
309	intel_screen_private                    *intel = intel_get_screen_private(scrn);
310	struct intel_present_vblank_event       *event;
311	int                                     pipe = intel_present_crtc_pipe(screen, crtc);
312	dri_bo                                  *bo;
313	Bool                                    ret;
314
315	if (!intel_present_check_flip(crtc, screen->root, pixmap, sync_flip))
316		return FALSE;
317
318	bo = intel_get_pixmap_bo(pixmap);
319	if (!bo)
320		return FALSE;
321
322	event = calloc(1, sizeof(struct intel_present_vblank_event));
323	if (!event)
324		return FALSE;
325
326	event->event_id = event_id;
327
328	ret = intel_do_pageflip(intel, bo, pipe, !sync_flip,
329				event,
330				intel_present_flip_event,
331				intel_present_flip_abort);
332	if (!ret)
333		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
334			   "present flip failed\n");
335	return ret;
336}
337
338/*
339 * Queue a flip back to the normal frame buffer
340 */
341static void
342intel_present_unflip(ScreenPtr screen, uint64_t event_id)
343{
344	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
345	intel_screen_private                    *intel = intel_get_screen_private(scrn);
346	struct intel_present_vblank_event       *event;
347	PixmapPtr                               pixmap = screen->GetScreenPixmap(screen);
348	dri_bo                                  *bo;
349	Bool                                    ret;
350
351	if (!intel_present_check_flip(NULL, screen->root, pixmap, true))
352		return;
353
354	bo = intel_get_pixmap_bo(pixmap);
355	if (!bo)
356		return;
357
358	event = calloc(1, sizeof(struct intel_present_vblank_event));
359	if (!event)
360		return;
361
362	event->event_id = event_id;
363
364	ret = intel_do_pageflip(intel, bo, -1, FALSE, event, intel_present_flip_event, intel_present_flip_abort);
365	if (!ret) {
366		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
367			   "present unflip failed\n");
368	}
369}
370
371static present_screen_info_rec intel_present_screen_info = {
372	.version = PRESENT_SCREEN_INFO_VERSION,
373
374	.get_crtc = intel_present_get_crtc,
375	.get_ust_msc = intel_present_get_ust_msc,
376	.queue_vblank = intel_present_queue_vblank,
377	.abort_vblank = intel_present_abort_vblank,
378	.flush = intel_present_flush,
379
380	.capabilities = PresentCapabilityNone,
381	.check_flip = intel_present_check_flip,
382	.flip = intel_present_flip,
383	.unflip = intel_present_unflip,
384};
385
386static Bool
387intel_present_has_async_flip(ScreenPtr screen)
388{
389#ifdef DRM_CAP_ASYNC_PAGE_FLIP
390	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
391	intel_screen_private    *intel = intel_get_screen_private(scrn);
392	int                     ret;
393	uint64_t                value;
394
395	ret = drmGetCap(intel->drmSubFD, DRM_CAP_ASYNC_PAGE_FLIP, &value);
396	if (ret == 0)
397		return value == 1;
398#endif
399	return FALSE;
400}
401
402Bool
403intel_present_screen_init(ScreenPtr screen)
404{
405	if (intel_present_has_async_flip(screen))
406		intel_present_screen_info.capabilities |= PresentCapabilityAsync;
407
408	return present_screen_init(screen, &intel_present_screen_info);
409}
410