1fda9279dSmrg/*
2fda9279dSmrg * Copyright 2013 Red Hat Inc.
3fda9279dSmrg *
4fda9279dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5fda9279dSmrg * copy of this software and associated documentation files (the "Software"),
6fda9279dSmrg * to deal in the Software without restriction, including without limitation
7fda9279dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8fda9279dSmrg * and/or sell copies of the Software, and to permit persons to whom the
9fda9279dSmrg * Software is furnished to do so, subject to the following conditions:
10fda9279dSmrg *
11fda9279dSmrg * The above copyright notice and this permission notice shall be included in
12fda9279dSmrg * all copies or substantial portions of the Software.
13fda9279dSmrg *
14fda9279dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15fda9279dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16fda9279dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17fda9279dSmrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18fda9279dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19fda9279dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20fda9279dSmrg * OTHER DEALINGS IN THE SOFTWARE.
21fda9279dSmrg *
22fda9279dSmrg * Authors: Ben Skeggs <bskeggs@redhat.com>
23fda9279dSmrg */
24fda9279dSmrg
25fda9279dSmrg#include "nouveau_present.h"
2616ee1e9aSmrg#if defined(DRI3)
27fda9279dSmrg#include "nv_include.h"
28fda9279dSmrg#include "xf86drmMode.h"
29fda9279dSmrg
30fda9279dSmrgstruct nouveau_present {
31fda9279dSmrg	struct present_screen_info info;
32fda9279dSmrg};
33fda9279dSmrg
34fda9279dSmrgstatic RRCrtcPtr
35fda9279dSmrgnouveau_present_crtc(WindowPtr window)
36fda9279dSmrg{
3733adc6acSmrg	return randr_crtc_covering_drawable(&window->drawable);
38fda9279dSmrg}
39fda9279dSmrg
40fda9279dSmrgstatic int
41fda9279dSmrgnouveau_present_ust_msc(RRCrtcPtr rrcrtc, uint64_t *ust, uint64_t *msc)
42fda9279dSmrg{
43fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
44fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
45fda9279dSmrg	drmVBlank args;
46fda9279dSmrg	int ret;
47fda9279dSmrg
48fda9279dSmrg	args.request.type = DRM_VBLANK_RELATIVE;
49fda9279dSmrg	args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT;
50fda9279dSmrg	args.request.sequence = 0,
51fda9279dSmrg	args.request.signal = 0,
52fda9279dSmrg
53fda9279dSmrg	ret = drmWaitVBlank(pNv->dev->fd, &args);
54fda9279dSmrg	if (ret) {
55fda9279dSmrg		*ust = *msc = 0;
56fda9279dSmrg		return BadMatch;
57fda9279dSmrg	}
58fda9279dSmrg
59fda9279dSmrg	*ust = (CARD64)args.reply.tval_sec * 1000000 + args.reply.tval_usec;
60fda9279dSmrg	*msc = args.reply.sequence;
61fda9279dSmrg	return Success;
62fda9279dSmrg}
63fda9279dSmrg
64fda9279dSmrgstruct nouveau_present_vblank {
65fda9279dSmrg	uint64_t msc;
66fda9279dSmrg};
67fda9279dSmrg
68fda9279dSmrgstatic void
69fda9279dSmrgnouveau_present_vblank(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo)
70fda9279dSmrg{
71fda9279dSmrg	struct nouveau_present_vblank *event = priv;
72fda9279dSmrg	uint64_t msc;
73fda9279dSmrg
74fda9279dSmrg	msc = (event->msc & 0xffffffff00000000ULL) | msc_lo;
75fda9279dSmrg	if (msc < event->msc)
76fda9279dSmrg		event->msc += 1ULL << 32;
77fda9279dSmrg
78fda9279dSmrg	present_event_notify(name, ust, msc);
79fda9279dSmrg}
80fda9279dSmrg
81fda9279dSmrgstatic int
82fda9279dSmrgnouveau_present_vblank_queue(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc)
83fda9279dSmrg{
84fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
85fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
86fda9279dSmrg	drmVBlank args;
87fda9279dSmrg	struct nouveau_present_vblank *event;
88fda9279dSmrg	void *token;
89fda9279dSmrg	int ret;
90fda9279dSmrg
91fda9279dSmrg	event = drmmode_event_queue(crtc->scrn, event_id, sizeof(*event),
92fda9279dSmrg				    nouveau_present_vblank, &token);
93fda9279dSmrg	if (!event)
94fda9279dSmrg		return BadAlloc;
95fda9279dSmrg
96fda9279dSmrg	event->msc = msc;
97fda9279dSmrg
98fda9279dSmrg	args.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
99fda9279dSmrg	args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT;
100fda9279dSmrg	args.request.sequence = msc;
101fda9279dSmrg	args.request.signal = (unsigned long)token;
102fda9279dSmrg
103fda9279dSmrg	while ((ret = drmWaitVBlank(pNv->dev->fd, &args)) != 0) {
10492405695Smrg		if (errno != EBUSY) {
10592405695Smrg			xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4,
10692405695Smrg				   "PRESENT: Wait for VBlank failed: %s\n", strerror(errno));
10792405695Smrg			drmmode_event_abort(crtc->scrn, event_id, false);
108fda9279dSmrg			return BadAlloc;
10992405695Smrg		}
11092405695Smrg		ret = drmmode_event_flush(crtc->scrn);
11192405695Smrg		if (ret < 0) {
11292405695Smrg			xf86DrvMsgVerb(crtc->scrn->scrnIndex, X_WARNING, 4,
11392405695Smrg				   "PRESENT: Event flush failed\n");
11492405695Smrg			drmmode_event_abort(crtc->scrn, event_id, false);
11592405695Smrg			return BadAlloc;
11692405695Smrg		}
117fda9279dSmrg	}
118fda9279dSmrg
119fda9279dSmrg	return Success;
120fda9279dSmrg}
121fda9279dSmrg
122fda9279dSmrgstatic void
123fda9279dSmrgnouveau_present_vblank_abort(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc)
124fda9279dSmrg{
125fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
126fda9279dSmrg	drmmode_event_abort(crtc->scrn, event_id, true);
127fda9279dSmrg}
128fda9279dSmrg
129fda9279dSmrgstatic void
130fda9279dSmrgnouveau_present_flush(WindowPtr window)
131fda9279dSmrg{
132fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
133fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
134fda9279dSmrg	if (pNv->Flush)
135fda9279dSmrg		pNv->Flush(scrn);
136fda9279dSmrg}
137fda9279dSmrg
138fda9279dSmrgstruct nouveau_present_flip {
139fda9279dSmrg	uint64_t msc;
140fda9279dSmrg	uint32_t old;
141fda9279dSmrg	int fd;
142fda9279dSmrg};
143fda9279dSmrg
144fda9279dSmrgstatic Bool
145fda9279dSmrgnouveau_present_flip_check(RRCrtcPtr rrcrtc, WindowPtr window,
146fda9279dSmrg			   PixmapPtr pixmap, Bool sync_flip)
147fda9279dSmrg{
148fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
14992405695Smrg	NVPtr pNv = NVPTR(scrn);
150fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
15192405695Smrg	struct nouveau_pixmap *priv = nouveau_pixmap(pixmap);
152fda9279dSmrg
15333adc6acSmrg	if (!scrn->vtSema || !xf86_crtc_on(crtc) || crtc->rotatedData)
154fda9279dSmrg		return FALSE;
155fda9279dSmrg
15692405695Smrg	if (!priv) {
15792405695Smrg		/* The pixmap may not have had backing for low-memory GPUs, or
15892405695Smrg		 * if we ran out of VRAM. Make sure it's properly backed for
15992405695Smrg		 * flipping.
16092405695Smrg		 */
16192405695Smrg		pNv->exa_force_cp = TRUE;
16292405695Smrg		exaMoveInPixmap(pixmap);
16392405695Smrg		pNv->exa_force_cp = FALSE;
16492405695Smrg		priv = nouveau_pixmap(pixmap);
16592405695Smrg	}
16692405695Smrg
16792405695Smrg	return priv ? TRUE : FALSE;
168fda9279dSmrg}
169fda9279dSmrg
170fda9279dSmrgstatic void
171fda9279dSmrgnouveau_present_flip(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo)
172fda9279dSmrg{
173fda9279dSmrg	struct nouveau_present_flip *flip = priv;
174fda9279dSmrg	uint64_t msc;
175fda9279dSmrg
176fda9279dSmrg	msc = (flip->msc & ~0xffffffffULL) | msc_lo;
177fda9279dSmrg	if (msc < flip->msc)
178fda9279dSmrg		msc += 1ULL << 32;
179fda9279dSmrg
180fda9279dSmrg	present_event_notify(name, ust, msc);
181fda9279dSmrg	drmModeRmFB(flip->fd, flip->old);
182fda9279dSmrg}
183fda9279dSmrg
184fda9279dSmrgstatic Bool
185fda9279dSmrgnouveau_present_flip_exec(ScrnInfoPtr scrn, uint64_t event_id, int sync,
186fda9279dSmrg			  uint64_t target_msc, PixmapPtr pixmap, Bool vsync)
187fda9279dSmrg{
18816ee1e9aSmrg	struct nouveau_pixmap *priv = nouveau_pixmap(pixmap);
189fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
190fda9279dSmrg	uint32_t next_fb;
191fda9279dSmrg	void *token;
192fda9279dSmrg	int ret;
193fda9279dSmrg
194fda9279dSmrg	ret = drmModeAddFB(pNv->dev->fd, pixmap->drawable.width,
195fda9279dSmrg			   pixmap->drawable.height, pixmap->drawable.depth,
196fda9279dSmrg			   pixmap->drawable.bitsPerPixel, pixmap->devKind,
197fda9279dSmrg			   priv->bo->handle, &next_fb);
198fda9279dSmrg	if (ret == 0) {
199fda9279dSmrg		struct nouveau_present_flip *flip =
200fda9279dSmrg			drmmode_event_queue(scrn, event_id, sizeof(*flip),
201fda9279dSmrg					    nouveau_present_flip, &token);
202fda9279dSmrg		if (flip) {
203fda9279dSmrg			xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
204fda9279dSmrg			int last = 0, i;
205fda9279dSmrg
206fda9279dSmrg			drmmode_swap(scrn, next_fb, &flip->old);
207fda9279dSmrg			flip->fd = pNv->dev->fd;
208fda9279dSmrg			flip->msc = target_msc;
209fda9279dSmrg
210fda9279dSmrg			for (i = 0; i < config->num_crtc; i++) {
21133adc6acSmrg				if (xf86_crtc_on(config->crtc[i]))
212fda9279dSmrg					last = i;
213fda9279dSmrg			}
214fda9279dSmrg
215fda9279dSmrg			for (i = 0; i < config->num_crtc; i++) {
216fda9279dSmrg				int type = vsync ? 0 : DRM_MODE_PAGE_FLIP_ASYNC;
217fda9279dSmrg				int crtc = drmmode_crtc(config->crtc[i]);
218fda9279dSmrg				void *user = NULL;
219fda9279dSmrg
22033adc6acSmrg				if (!xf86_crtc_on(config->crtc[i]))
221fda9279dSmrg					continue;
222fda9279dSmrg
223fda9279dSmrg				if (token && ((crtc == sync) || (i == last))) {
224fda9279dSmrg					type |= DRM_MODE_PAGE_FLIP_EVENT;
225fda9279dSmrg					user  = token;
226fda9279dSmrg				}
227fda9279dSmrg
228fda9279dSmrg				ret = drmModePageFlip(pNv->dev->fd, crtc,
229fda9279dSmrg						      next_fb, type, user);
230fda9279dSmrg				if (ret == 0 && user) {
231fda9279dSmrg					token = NULL;
232fda9279dSmrg				}
233fda9279dSmrg			}
234fda9279dSmrg
235fda9279dSmrg			if (token == NULL) {
236fda9279dSmrg				return TRUE;
237fda9279dSmrg			}
238fda9279dSmrg
239fda9279dSmrg			drmmode_swap(scrn, flip->old, &next_fb);
240fda9279dSmrg			drmmode_event_abort(scrn, event_id, false);
241fda9279dSmrg		}
242fda9279dSmrg
243fda9279dSmrg		drmModeRmFB(pNv->dev->fd, next_fb);
244fda9279dSmrg	}
245fda9279dSmrg
246fda9279dSmrg	return FALSE;
247fda9279dSmrg}
248fda9279dSmrg
249fda9279dSmrgstatic Bool
250fda9279dSmrgnouveau_present_flip_next(RRCrtcPtr rrcrtc, uint64_t event_id,
251fda9279dSmrg			  uint64_t target_msc, PixmapPtr pixmap, Bool vsync)
252fda9279dSmrg{
253fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
254fda9279dSmrg	ScrnInfoPtr scrn = crtc->scrn;
255fda9279dSmrg	return nouveau_present_flip_exec(scrn, event_id, drmmode_crtc(crtc),
256fda9279dSmrg					 target_msc, pixmap, vsync);
257fda9279dSmrg}
258fda9279dSmrg
259fda9279dSmrgstatic void
260fda9279dSmrgnouveau_present_flip_stop(ScreenPtr screen, uint64_t event_id)
261fda9279dSmrg{
262fda9279dSmrg	PixmapPtr pixmap = screen->GetScreenPixmap(screen);
263fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
264fda9279dSmrg	nouveau_present_flip_exec(scrn, event_id, 0, 0, pixmap, TRUE);
265fda9279dSmrg}
266fda9279dSmrg
267fda9279dSmrgvoid
268fda9279dSmrgnouveau_present_fini(ScreenPtr screen)
269fda9279dSmrg{
270fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
271fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
272fda9279dSmrg	if (pNv->present) {
273fda9279dSmrg		free(pNv->present);
274fda9279dSmrg		pNv->present = NULL;
275fda9279dSmrg	}
276fda9279dSmrg}
277fda9279dSmrg
27816ee1e9aSmrgBool
279fda9279dSmrgnouveau_present_init(ScreenPtr screen)
280fda9279dSmrg{
281fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
282fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
283fda9279dSmrg	struct nouveau_present *present;
284fda9279dSmrg	uint64_t value;
285fda9279dSmrg	int ret;
286fda9279dSmrg
287fda9279dSmrg	present = pNv->present = calloc(1, sizeof(*present));
288fda9279dSmrg	if (!present)
28916ee1e9aSmrg		return FALSE;
290fda9279dSmrg
291fda9279dSmrg	present->info.version = PRESENT_SCREEN_INFO_VERSION;
292fda9279dSmrg	present->info.get_crtc = nouveau_present_crtc;
293fda9279dSmrg	present->info.get_ust_msc = nouveau_present_ust_msc;
294fda9279dSmrg	present->info.queue_vblank = nouveau_present_vblank_queue;
295fda9279dSmrg	present->info.abort_vblank = nouveau_present_vblank_abort;
296fda9279dSmrg	present->info.flush = nouveau_present_flush;
297fda9279dSmrg
298fda9279dSmrg	if (pNv->has_pageflip) {
299fda9279dSmrg#ifdef DRM_CAP_ASYNC_PAGE_FLIP
300fda9279dSmrg		ret = drmGetCap(pNv->dev->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
301fda9279dSmrg		if (ret == 0 && value == 1)
302fda9279dSmrg			present->info.capabilities |= PresentCapabilityAsync;
303fda9279dSmrg#endif
304fda9279dSmrg		present->info.check_flip = nouveau_present_flip_check;
305fda9279dSmrg		present->info.flip = nouveau_present_flip_next;
306fda9279dSmrg		present->info.unflip = nouveau_present_flip_stop;
307fda9279dSmrg	}
308fda9279dSmrg
309fda9279dSmrg	return present_screen_init(screen, &present->info);
310fda9279dSmrg}
311fda9279dSmrg#endif
312