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	uint32_t		tiling, swizzle;
248
249	if (!scrn->vtSema)
250		return FALSE;
251
252	if (intel->shadow_present)
253		return FALSE;
254
255	if (!intel->use_pageflipping)
256		return FALSE;
257
258	if (crtc && !intel_crtc_on(crtc->devPrivate))
259		return FALSE;
260
261        /* Check stride, can't change that on flip */
262        if (pixmap->devKind != intel->front_pitch)
263                return FALSE;
264
265        /* Make sure there's a bo we can get to */
266        bo = intel_get_pixmap_bo(pixmap);
267        if (!bo)
268                return FALSE;
269
270	if (drm_intel_bo_get_tiling(bo, &tiling, &swizzle))
271		return FALSE;
272
273	if (tiling == I915_TILING_Y)
274		return FALSE;
275
276	return TRUE;
277}
278
279/*
280 * Once the flip has been completed on all pipes, notify the
281 * extension code telling it when that happened
282 */
283static void
284intel_present_flip_event(uint64_t msc, uint64_t ust, void *pageflip_data)
285{
286	struct intel_present_vblank_event *event = pageflip_data;
287
288	present_event_notify(event->event_id, ust, msc);
289	free(event);
290}
291
292/*
293 * The flip has been aborted, free the structure
294 */
295static void
296intel_present_flip_abort(void *pageflip_data)
297{
298	struct intel_present_vblank_event *event = pageflip_data;
299
300	free(event);
301}
302
303/*
304 * Queue a flip on 'crtc' to 'pixmap' at 'target_msc'. If 'sync_flip' is true,
305 * then wait for vblank. Otherwise, flip immediately
306 */
307static Bool
308intel_present_flip(RRCrtcPtr                    crtc,
309                   uint64_t                     event_id,
310                   uint64_t                     target_msc,
311                   PixmapPtr                    pixmap,
312                   Bool                         sync_flip)
313{
314	ScreenPtr                               screen = crtc->pScreen;
315	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
316	intel_screen_private                    *intel = intel_get_screen_private(scrn);
317	struct intel_present_vblank_event       *event;
318	int                                     pipe = intel_present_crtc_pipe(screen, crtc);
319	dri_bo                                  *bo;
320	Bool                                    ret;
321
322	if (!intel_present_check_flip(crtc, screen->root, pixmap, sync_flip))
323		return FALSE;
324
325	bo = intel_get_pixmap_bo(pixmap);
326	if (!bo)
327		return FALSE;
328
329	event = calloc(1, sizeof(struct intel_present_vblank_event));
330	if (!event)
331		return FALSE;
332
333	event->event_id = event_id;
334
335	ret = intel_do_pageflip(intel, bo, pipe, !sync_flip,
336				event,
337				intel_present_flip_event,
338				intel_present_flip_abort);
339	if (!ret)
340		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
341			   "present flip failed\n");
342	return ret;
343}
344
345/*
346 * Queue a flip back to the normal frame buffer
347 */
348static void
349intel_present_unflip(ScreenPtr screen, uint64_t event_id)
350{
351	ScrnInfoPtr                             scrn = xf86ScreenToScrn(screen);
352	intel_screen_private                    *intel = intel_get_screen_private(scrn);
353	PixmapPtr                               pixmap = screen->GetScreenPixmap(screen);
354	struct intel_present_vblank_event       *event = NULL;
355	dri_bo                                  *bo;
356
357	if (!intel_present_check_flip(NULL, screen->root, pixmap, true))
358		goto fail;
359
360	bo = intel_get_pixmap_bo(pixmap);
361	if (!bo)
362		goto fail;
363
364	event = calloc(1, sizeof(struct intel_present_vblank_event));
365	if (!event)
366		goto fail;
367
368	event->event_id = event_id;
369
370	if (!intel_do_pageflip(intel, bo, -1, FALSE, event,
371			       intel_present_flip_event,
372			       intel_present_flip_abort))
373		goto fail;
374
375	return;
376fail:
377	xf86SetDesiredModes(scrn);
378	present_event_notify(event_id, 0, 0);
379	free(event);
380}
381
382static present_screen_info_rec intel_present_screen_info = {
383	.version = PRESENT_SCREEN_INFO_VERSION,
384
385	.get_crtc = intel_present_get_crtc,
386	.get_ust_msc = intel_present_get_ust_msc,
387	.queue_vblank = intel_present_queue_vblank,
388	.abort_vblank = intel_present_abort_vblank,
389	.flush = intel_present_flush,
390
391	.capabilities = PresentCapabilityNone,
392	.check_flip = intel_present_check_flip,
393	.flip = intel_present_flip,
394	.unflip = intel_present_unflip,
395};
396
397static Bool
398intel_present_has_async_flip(ScreenPtr screen)
399{
400#ifdef DRM_CAP_ASYNC_PAGE_FLIP
401	ScrnInfoPtr             scrn = xf86ScreenToScrn(screen);
402	intel_screen_private    *intel = intel_get_screen_private(scrn);
403	int                     ret;
404	uint64_t                value;
405
406	ret = drmGetCap(intel->drmSubFD, DRM_CAP_ASYNC_PAGE_FLIP, &value);
407	if (ret == 0)
408		return value == 1;
409#endif
410	return FALSE;
411}
412
413Bool
414intel_present_screen_init(ScreenPtr screen)
415{
416	if (intel_present_has_async_flip(screen))
417		intel_present_screen_info.capabilities |= PresentCapabilityAsync;
418
419	return present_screen_init(screen, &intel_present_screen_info);
420}
421