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