nouveau_present.c revision 16ee1e9a
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{
37fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
38fda9279dSmrg	xf86CrtcPtr crtc;
39fda9279dSmrg
40fda9279dSmrg	crtc = nouveau_pick_best_crtc(scrn, FALSE,
41fda9279dSmrg                                  window->drawable.x,
42fda9279dSmrg                                  window->drawable.y,
43fda9279dSmrg                                  window->drawable.width,
44fda9279dSmrg                                  window->drawable.height);
45fda9279dSmrg
46fda9279dSmrg	if (!crtc)
47fda9279dSmrg		return NULL;
48fda9279dSmrg
49fda9279dSmrg	if (crtc->rotatedData)
50fda9279dSmrg		return NULL;
51fda9279dSmrg
52fda9279dSmrg	return crtc->randr_crtc;
53fda9279dSmrg}
54fda9279dSmrg
55fda9279dSmrgstatic int
56fda9279dSmrgnouveau_present_ust_msc(RRCrtcPtr rrcrtc, uint64_t *ust, uint64_t *msc)
57fda9279dSmrg{
58fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
59fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
60fda9279dSmrg	drmVBlank args;
61fda9279dSmrg	int ret;
62fda9279dSmrg
63fda9279dSmrg	args.request.type = DRM_VBLANK_RELATIVE;
64fda9279dSmrg	args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT;
65fda9279dSmrg	args.request.sequence = 0,
66fda9279dSmrg	args.request.signal = 0,
67fda9279dSmrg
68fda9279dSmrg	ret = drmWaitVBlank(pNv->dev->fd, &args);
69fda9279dSmrg	if (ret) {
70fda9279dSmrg		*ust = *msc = 0;
71fda9279dSmrg		return BadMatch;
72fda9279dSmrg	}
73fda9279dSmrg
74fda9279dSmrg	*ust = (CARD64)args.reply.tval_sec * 1000000 + args.reply.tval_usec;
75fda9279dSmrg	*msc = args.reply.sequence;
76fda9279dSmrg	return Success;
77fda9279dSmrg}
78fda9279dSmrg
79fda9279dSmrgstruct nouveau_present_vblank {
80fda9279dSmrg	uint64_t msc;
81fda9279dSmrg};
82fda9279dSmrg
83fda9279dSmrgstatic void
84fda9279dSmrgnouveau_present_vblank(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo)
85fda9279dSmrg{
86fda9279dSmrg	struct nouveau_present_vblank *event = priv;
87fda9279dSmrg	uint64_t msc;
88fda9279dSmrg
89fda9279dSmrg	msc = (event->msc & 0xffffffff00000000ULL) | msc_lo;
90fda9279dSmrg	if (msc < event->msc)
91fda9279dSmrg		event->msc += 1ULL << 32;
92fda9279dSmrg
93fda9279dSmrg	present_event_notify(name, ust, msc);
94fda9279dSmrg}
95fda9279dSmrg
96fda9279dSmrgstatic int
97fda9279dSmrgnouveau_present_vblank_queue(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc)
98fda9279dSmrg{
99fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
100fda9279dSmrg	NVPtr pNv = NVPTR(crtc->scrn);
101fda9279dSmrg	drmVBlank args;
102fda9279dSmrg	struct nouveau_present_vblank *event;
103fda9279dSmrg	void *token;
104fda9279dSmrg	int ret;
105fda9279dSmrg
106fda9279dSmrg	event = drmmode_event_queue(crtc->scrn, event_id, sizeof(*event),
107fda9279dSmrg				    nouveau_present_vblank, &token);
108fda9279dSmrg	if (!event)
109fda9279dSmrg		return BadAlloc;
110fda9279dSmrg
111fda9279dSmrg	event->msc = msc;
112fda9279dSmrg
113fda9279dSmrg	args.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
114fda9279dSmrg	args.request.type |= drmmode_head(crtc) << DRM_VBLANK_HIGH_CRTC_SHIFT;
115fda9279dSmrg	args.request.sequence = msc;
116fda9279dSmrg	args.request.signal = (unsigned long)token;
117fda9279dSmrg
118fda9279dSmrg	while ((ret = drmWaitVBlank(pNv->dev->fd, &args)) != 0) {
119fda9279dSmrg		if (errno != EBUSY || drmmode_event_flush(crtc->scrn) < 0)
120fda9279dSmrg			return BadAlloc;
121fda9279dSmrg	}
122fda9279dSmrg
123fda9279dSmrg	return Success;
124fda9279dSmrg}
125fda9279dSmrg
126fda9279dSmrgstatic void
127fda9279dSmrgnouveau_present_vblank_abort(RRCrtcPtr rrcrtc, uint64_t event_id, uint64_t msc)
128fda9279dSmrg{
129fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
130fda9279dSmrg	drmmode_event_abort(crtc->scrn, event_id, true);
131fda9279dSmrg}
132fda9279dSmrg
133fda9279dSmrgstatic void
134fda9279dSmrgnouveau_present_flush(WindowPtr window)
135fda9279dSmrg{
136fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
137fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
138fda9279dSmrg	if (pNv->Flush)
139fda9279dSmrg		pNv->Flush(scrn);
140fda9279dSmrg}
141fda9279dSmrg
142fda9279dSmrgstruct nouveau_present_flip {
143fda9279dSmrg	uint64_t msc;
144fda9279dSmrg	uint32_t old;
145fda9279dSmrg	int fd;
146fda9279dSmrg};
147fda9279dSmrg
148fda9279dSmrgstatic Bool
149fda9279dSmrgnouveau_present_flip_check(RRCrtcPtr rrcrtc, WindowPtr window,
150fda9279dSmrg			   PixmapPtr pixmap, Bool sync_flip)
151fda9279dSmrg{
152fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
153fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
154fda9279dSmrg
155fda9279dSmrg	if (!scrn->vtSema || !crtc->enabled)
156fda9279dSmrg		return FALSE;
157fda9279dSmrg
158fda9279dSmrg	return TRUE;
159fda9279dSmrg}
160fda9279dSmrg
161fda9279dSmrgstatic void
162fda9279dSmrgnouveau_present_flip(void *priv, uint64_t name, uint64_t ust, uint32_t msc_lo)
163fda9279dSmrg{
164fda9279dSmrg	struct nouveau_present_flip *flip = priv;
165fda9279dSmrg	uint64_t msc;
166fda9279dSmrg
167fda9279dSmrg	msc = (flip->msc & ~0xffffffffULL) | msc_lo;
168fda9279dSmrg	if (msc < flip->msc)
169fda9279dSmrg		msc += 1ULL << 32;
170fda9279dSmrg
171fda9279dSmrg	present_event_notify(name, ust, msc);
172fda9279dSmrg	drmModeRmFB(flip->fd, flip->old);
173fda9279dSmrg}
174fda9279dSmrg
175fda9279dSmrgstatic Bool
176fda9279dSmrgnouveau_present_flip_exec(ScrnInfoPtr scrn, uint64_t event_id, int sync,
177fda9279dSmrg			  uint64_t target_msc, PixmapPtr pixmap, Bool vsync)
178fda9279dSmrg{
17916ee1e9aSmrg	struct nouveau_pixmap *priv = nouveau_pixmap(pixmap);
180fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
181fda9279dSmrg	uint32_t next_fb;
182fda9279dSmrg	void *token;
183fda9279dSmrg	int ret;
184fda9279dSmrg
185fda9279dSmrg	ret = drmModeAddFB(pNv->dev->fd, pixmap->drawable.width,
186fda9279dSmrg			   pixmap->drawable.height, pixmap->drawable.depth,
187fda9279dSmrg			   pixmap->drawable.bitsPerPixel, pixmap->devKind,
188fda9279dSmrg			   priv->bo->handle, &next_fb);
189fda9279dSmrg	if (ret == 0) {
190fda9279dSmrg		struct nouveau_present_flip *flip =
191fda9279dSmrg			drmmode_event_queue(scrn, event_id, sizeof(*flip),
192fda9279dSmrg					    nouveau_present_flip, &token);
193fda9279dSmrg		if (flip) {
194fda9279dSmrg			xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
195fda9279dSmrg			int last = 0, i;
196fda9279dSmrg
197fda9279dSmrg			drmmode_swap(scrn, next_fb, &flip->old);
198fda9279dSmrg			flip->fd = pNv->dev->fd;
199fda9279dSmrg			flip->msc = target_msc;
200fda9279dSmrg
201fda9279dSmrg			for (i = 0; i < config->num_crtc; i++) {
202fda9279dSmrg				if (config->crtc[i]->enabled)
203fda9279dSmrg					last = i;
204fda9279dSmrg			}
205fda9279dSmrg
206fda9279dSmrg			for (i = 0; i < config->num_crtc; i++) {
207fda9279dSmrg				int type = vsync ? 0 : DRM_MODE_PAGE_FLIP_ASYNC;
208fda9279dSmrg				int crtc = drmmode_crtc(config->crtc[i]);
209fda9279dSmrg				void *user = NULL;
210fda9279dSmrg
211fda9279dSmrg				if (!config->crtc[i]->enabled)
212fda9279dSmrg					continue;
213fda9279dSmrg
214fda9279dSmrg				if (token && ((crtc == sync) || (i == last))) {
215fda9279dSmrg					type |= DRM_MODE_PAGE_FLIP_EVENT;
216fda9279dSmrg					user  = token;
217fda9279dSmrg				}
218fda9279dSmrg
219fda9279dSmrg				ret = drmModePageFlip(pNv->dev->fd, crtc,
220fda9279dSmrg						      next_fb, type, user);
221fda9279dSmrg				if (ret == 0 && user) {
222fda9279dSmrg					token = NULL;
223fda9279dSmrg				}
224fda9279dSmrg			}
225fda9279dSmrg
226fda9279dSmrg			if (token == NULL) {
227fda9279dSmrg				return TRUE;
228fda9279dSmrg			}
229fda9279dSmrg
230fda9279dSmrg			drmmode_swap(scrn, flip->old, &next_fb);
231fda9279dSmrg			drmmode_event_abort(scrn, event_id, false);
232fda9279dSmrg		}
233fda9279dSmrg
234fda9279dSmrg		drmModeRmFB(pNv->dev->fd, next_fb);
235fda9279dSmrg	}
236fda9279dSmrg
237fda9279dSmrg	return FALSE;
238fda9279dSmrg}
239fda9279dSmrg
240fda9279dSmrgstatic Bool
241fda9279dSmrgnouveau_present_flip_next(RRCrtcPtr rrcrtc, uint64_t event_id,
242fda9279dSmrg			  uint64_t target_msc, PixmapPtr pixmap, Bool vsync)
243fda9279dSmrg{
244fda9279dSmrg	xf86CrtcPtr crtc = rrcrtc->devPrivate;
245fda9279dSmrg	ScrnInfoPtr scrn = crtc->scrn;
246fda9279dSmrg	return nouveau_present_flip_exec(scrn, event_id, drmmode_crtc(crtc),
247fda9279dSmrg					 target_msc, pixmap, vsync);
248fda9279dSmrg}
249fda9279dSmrg
250fda9279dSmrgstatic void
251fda9279dSmrgnouveau_present_flip_stop(ScreenPtr screen, uint64_t event_id)
252fda9279dSmrg{
253fda9279dSmrg	PixmapPtr pixmap = screen->GetScreenPixmap(screen);
254fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
255fda9279dSmrg	nouveau_present_flip_exec(scrn, event_id, 0, 0, pixmap, TRUE);
256fda9279dSmrg}
257fda9279dSmrg
258fda9279dSmrgvoid
259fda9279dSmrgnouveau_present_fini(ScreenPtr screen)
260fda9279dSmrg{
261fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
262fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
263fda9279dSmrg	if (pNv->present) {
264fda9279dSmrg		free(pNv->present);
265fda9279dSmrg		pNv->present = NULL;
266fda9279dSmrg	}
267fda9279dSmrg}
268fda9279dSmrg
26916ee1e9aSmrgBool
270fda9279dSmrgnouveau_present_init(ScreenPtr screen)
271fda9279dSmrg{
272fda9279dSmrg	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
273fda9279dSmrg	NVPtr pNv = NVPTR(scrn);
274fda9279dSmrg	struct nouveau_present *present;
275fda9279dSmrg	uint64_t value;
276fda9279dSmrg	int ret;
277fda9279dSmrg
278fda9279dSmrg	present = pNv->present = calloc(1, sizeof(*present));
279fda9279dSmrg	if (!present)
28016ee1e9aSmrg		return FALSE;
281fda9279dSmrg
282fda9279dSmrg	present->info.version = PRESENT_SCREEN_INFO_VERSION;
283fda9279dSmrg	present->info.get_crtc = nouveau_present_crtc;
284fda9279dSmrg	present->info.get_ust_msc = nouveau_present_ust_msc;
285fda9279dSmrg	present->info.queue_vblank = nouveau_present_vblank_queue;
286fda9279dSmrg	present->info.abort_vblank = nouveau_present_vblank_abort;
287fda9279dSmrg	present->info.flush = nouveau_present_flush;
288fda9279dSmrg
289fda9279dSmrg	if (pNv->has_pageflip) {
290fda9279dSmrg#ifdef DRM_CAP_ASYNC_PAGE_FLIP
291fda9279dSmrg		ret = drmGetCap(pNv->dev->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
292fda9279dSmrg		if (ret == 0 && value == 1)
293fda9279dSmrg			present->info.capabilities |= PresentCapabilityAsync;
294fda9279dSmrg#endif
295fda9279dSmrg		present->info.check_flip = nouveau_present_flip_check;
296fda9279dSmrg		present->info.flip = nouveau_present_flip_next;
297fda9279dSmrg		present->info.unflip = nouveau_present_flip_stop;
298fda9279dSmrg	}
299fda9279dSmrg
300fda9279dSmrg	return present_screen_init(screen, &present->info);
301fda9279dSmrg}
302fda9279dSmrg#endif
303