1/*
2 * Copyright © 2007 Red Hat, Inc.
3 * Copyright © 2015 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 *    Dave Airlie <airlied@redhat.com>
26 *
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <errno.h>
34
35#include <xorg-server.h>
36#include <X11/Xdefs.h>
37#include <list.h>
38
39#include "amdgpu_drv.h"
40#include "amdgpu_drm_queue.h"
41
42
43struct amdgpu_drm_queue_entry {
44	struct xorg_list list;
45	uint64_t usec;
46	uint64_t id;
47	uintptr_t seq;
48	void *data;
49	ClientPtr client;
50	xf86CrtcPtr crtc;
51	amdgpu_drm_handler_proc handler;
52	amdgpu_drm_abort_proc abort;
53	Bool is_flip;
54	unsigned int frame;
55};
56
57static int amdgpu_drm_queue_refcnt;
58static struct xorg_list amdgpu_drm_queue;
59static struct xorg_list amdgpu_drm_flip_signalled;
60static struct xorg_list amdgpu_drm_vblank_signalled;
61static struct xorg_list amdgpu_drm_vblank_deferred;
62static uintptr_t amdgpu_drm_queue_seq;
63
64
65/*
66 * Process a DRM event
67 */
68static void
69amdgpu_drm_queue_handle_one(struct amdgpu_drm_queue_entry *e)
70{
71	xorg_list_del(&e->list);
72	if (e->handler) {
73		e->handler(e->crtc, e->frame, e->usec, e->data);
74	} else
75		e->abort(e->crtc, e->data);
76	free(e);
77}
78
79/*
80 * Abort one queued DRM entry, removing it
81 * from the list, calling the abort function and
82 * freeing the memory
83 */
84static void
85amdgpu_drm_abort_one(struct amdgpu_drm_queue_entry *e)
86{
87	xorg_list_del(&e->list);
88	e->abort(e->crtc, e->data);
89	free(e);
90}
91
92static void
93amdgpu_drm_queue_handler(int fd, unsigned int frame, unsigned int sec,
94			 unsigned int usec, void *user_ptr)
95{
96	uintptr_t seq = (uintptr_t)user_ptr;
97	struct amdgpu_drm_queue_entry *e, *tmp;
98
99	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
100		if (e->seq == seq) {
101			if (!e->handler) {
102				amdgpu_drm_abort_one(e);
103				break;
104			}
105
106			xorg_list_del(&e->list);
107			e->usec = (uint64_t)sec * 1000000 + usec;
108			e->frame = frame;
109			xorg_list_append(&e->list, e->is_flip ?
110					 &amdgpu_drm_flip_signalled :
111					 &amdgpu_drm_vblank_signalled);
112			break;
113		}
114	}
115}
116
117/*
118 * Handle signalled vblank events. If we're waiting for a flip event,
119 * put events for that CRTC in the vblank_deferred list.
120 */
121static void
122amdgpu_drm_handle_vblank_signalled(void)
123{
124	drmmode_crtc_private_ptr drmmode_crtc;
125	struct amdgpu_drm_queue_entry *e;
126
127	while (!xorg_list_is_empty(&amdgpu_drm_vblank_signalled)) {
128		e = xorg_list_first_entry(&amdgpu_drm_vblank_signalled,
129					  struct amdgpu_drm_queue_entry, list);
130		drmmode_crtc = e->crtc->driver_private;
131
132		if (drmmode_crtc->wait_flip_nesting_level == 0) {
133			amdgpu_drm_queue_handle_one(e);
134			continue;
135		}
136
137		xorg_list_del(&e->list);
138		xorg_list_append(&e->list, &amdgpu_drm_vblank_deferred);
139	}
140}
141
142/*
143 * Handle deferred DRM vblank events
144 *
145 * This function must be called after amdgpu_drm_wait_pending_flip, once
146 * it's safe to attempt queueing a flip again
147 */
148void
149amdgpu_drm_queue_handle_deferred(xf86CrtcPtr crtc)
150{
151	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
152	struct amdgpu_drm_queue_entry *e, *tmp;
153
154	if (drmmode_crtc->wait_flip_nesting_level == 0 ||
155	    --drmmode_crtc->wait_flip_nesting_level > 0)
156		return;
157
158	/* Put previously deferred vblank events for this CRTC back in the
159	 * signalled queue
160	 */
161	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_vblank_deferred, list) {
162		if (e->crtc != crtc)
163			continue;
164
165		xorg_list_del(&e->list);
166		xorg_list_append(&e->list, &amdgpu_drm_vblank_signalled);
167	}
168
169	amdgpu_drm_handle_vblank_signalled();
170}
171
172/*
173 * Enqueue a potential drm response; when the associated response
174 * appears, we've got data to pass to the handler from here
175 */
176uintptr_t
177amdgpu_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client,
178		       uint64_t id, void *data,
179		       amdgpu_drm_handler_proc handler,
180		       amdgpu_drm_abort_proc abort,
181		       Bool is_flip)
182{
183	struct amdgpu_drm_queue_entry *e;
184
185	e = calloc(1, sizeof(struct amdgpu_drm_queue_entry));
186	if (!e)
187		return AMDGPU_DRM_QUEUE_ERROR;
188
189	if (_X_UNLIKELY(amdgpu_drm_queue_seq == AMDGPU_DRM_QUEUE_ERROR))
190		amdgpu_drm_queue_seq++;
191
192	e->seq = amdgpu_drm_queue_seq++;
193	e->client = client;
194	e->crtc = crtc;
195	e->id = id;
196	e->data = data;
197	e->handler = handler;
198	e->abort = abort;
199	e->is_flip = is_flip;
200
201	xorg_list_append(&e->list, &amdgpu_drm_queue);
202
203	return e->seq;
204}
205
206/*
207 * Abort drm queue entries for a client
208 *
209 * NOTE: This keeps the entries in the list until the DRM event arrives,
210 * but then it calls the abort functions instead of the handler
211 * functions.
212 */
213void
214amdgpu_drm_abort_client(ClientPtr client)
215{
216	struct amdgpu_drm_queue_entry *e;
217
218	xorg_list_for_each_entry(e, &amdgpu_drm_queue, list) {
219		if (e->client == client)
220			e->handler = NULL;
221	}
222}
223
224/*
225 * Abort specific drm queue entry
226 */
227void
228amdgpu_drm_abort_entry(uintptr_t seq)
229{
230	struct amdgpu_drm_queue_entry *e, *tmp;
231
232	if (seq == AMDGPU_DRM_QUEUE_ERROR)
233		return;
234
235	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_vblank_signalled, list) {
236		if (e->seq == seq) {
237			amdgpu_drm_abort_one(e);
238			return;
239		}
240	}
241
242	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_vblank_deferred, list) {
243		if (e->seq == seq) {
244			amdgpu_drm_abort_one(e);
245			return;
246		}
247	}
248
249	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
250		if (e->seq == seq) {
251			amdgpu_drm_abort_one(e);
252			break;
253		}
254	}
255}
256
257/*
258 * Abort specific drm queue entry by ID
259 */
260void
261amdgpu_drm_abort_id(uint64_t id)
262{
263	struct amdgpu_drm_queue_entry *e, *tmp;
264
265	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
266		if (e->id == id) {
267			amdgpu_drm_abort_one(e);
268			break;
269		}
270	}
271}
272
273/*
274 * drmHandleEvent wrapper
275 */
276int
277amdgpu_drm_handle_event(int fd, drmEventContext *event_context)
278{
279	struct amdgpu_drm_queue_entry *e;
280	int r;
281
282	/* Retry drmHandleEvent if it was interrupted by a signal in read() */
283	do {
284		r = drmHandleEvent(fd, event_context);
285	} while (r < 0 && (errno == EINTR || errno == EAGAIN));
286
287	if (r < 0) {
288		static Bool printed;
289
290		if (!printed) {
291			ErrorF("%s: drmHandleEvent returned %d, errno=%d (%s)\n",
292			       __func__, r, errno, strerror(errno));
293			printed = TRUE;
294		}
295	}
296
297	while (!xorg_list_is_empty(&amdgpu_drm_flip_signalled)) {
298		e = xorg_list_first_entry(&amdgpu_drm_flip_signalled,
299					  struct amdgpu_drm_queue_entry, list);
300		amdgpu_drm_queue_handle_one(e);
301	}
302
303	amdgpu_drm_handle_vblank_signalled();
304
305	return r;
306}
307
308/*
309 * Wait for pending page flip on given CRTC to complete
310 */
311void amdgpu_drm_wait_pending_flip(xf86CrtcPtr crtc)
312{
313	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
314	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(crtc->scrn);
315	struct amdgpu_drm_queue_entry *e;
316
317	drmmode_crtc->wait_flip_nesting_level++;
318
319	while (drmmode_crtc->flip_pending &&
320	       !xorg_list_is_empty(&amdgpu_drm_flip_signalled)) {
321		e = xorg_list_first_entry(&amdgpu_drm_flip_signalled,
322					  struct amdgpu_drm_queue_entry, list);
323		amdgpu_drm_queue_handle_one(e);
324	}
325
326	while (drmmode_crtc->flip_pending
327	       && amdgpu_drm_handle_event(pAMDGPUEnt->fd,
328					  &drmmode_crtc->drmmode->event_context) >= 0);
329}
330
331/*
332 * Initialize the DRM event queue
333 */
334void
335amdgpu_drm_queue_init(ScrnInfoPtr scrn)
336{
337	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
338	drmmode_ptr drmmode = &info->drmmode;
339
340	drmmode->event_context.version = 2;
341	drmmode->event_context.vblank_handler = amdgpu_drm_queue_handler;
342	drmmode->event_context.page_flip_handler = amdgpu_drm_queue_handler;
343
344	if (amdgpu_drm_queue_refcnt++)
345		return;
346
347	xorg_list_init(&amdgpu_drm_queue);
348	xorg_list_init(&amdgpu_drm_flip_signalled);
349	xorg_list_init(&amdgpu_drm_vblank_signalled);
350	xorg_list_init(&amdgpu_drm_vblank_deferred);
351}
352
353/*
354 * Deinitialize the DRM event queue
355 */
356void
357amdgpu_drm_queue_close(ScrnInfoPtr scrn)
358{
359	struct amdgpu_drm_queue_entry *e, *tmp;
360
361	xorg_list_for_each_entry_safe(e, tmp, &amdgpu_drm_queue, list) {
362		if (e->crtc->scrn == scrn)
363			amdgpu_drm_abort_one(e);
364	}
365
366	amdgpu_drm_queue_refcnt--;
367}
368