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