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 "radeon.h"
40#include "radeon_drm_queue.h"
41
42
43struct radeon_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    radeon_drm_handler_proc handler;
52    radeon_drm_abort_proc abort;
53    Bool is_flip;
54    unsigned int frame;
55};
56
57static int radeon_drm_queue_refcnt;
58static struct xorg_list radeon_drm_queue;
59static struct xorg_list radeon_drm_flip_signalled;
60static struct xorg_list radeon_drm_vblank_signalled;
61static struct xorg_list radeon_drm_vblank_deferred;
62static uintptr_t radeon_drm_queue_seq;
63
64
65/*
66 * Process a DRM event
67 */
68static void
69radeon_drm_queue_handle_one(struct radeon_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
85radeon_drm_abort_one(struct radeon_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
93radeon_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 radeon_drm_queue_entry *e, *tmp;
98
99    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
100	if (e->seq == seq) {
101	    if (!e->handler) {
102		radeon_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			     &radeon_drm_flip_signalled :
111			     &radeon_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
122radeon_drm_handle_vblank_signalled(void)
123{
124    drmmode_crtc_private_ptr drmmode_crtc;
125    struct radeon_drm_queue_entry *e;
126
127    while (!xorg_list_is_empty(&radeon_drm_vblank_signalled)) {
128	e = xorg_list_first_entry(&radeon_drm_vblank_signalled,
129				  struct radeon_drm_queue_entry, list);
130	drmmode_crtc = e->crtc->driver_private;
131
132	if (drmmode_crtc->wait_flip_nesting_level == 0) {
133	    radeon_drm_queue_handle_one(e);
134	    continue;
135	}
136
137	xorg_list_del(&e->list);
138	xorg_list_append(&e->list, &radeon_drm_vblank_deferred);
139    }
140}
141
142/*
143 * Handle deferred DRM vblank events
144 *
145 * This function must be called after radeon_drm_wait_pending_flip, once
146 * it's safe to attempt queueing a flip again
147 */
148void
149radeon_drm_queue_handle_deferred(xf86CrtcPtr crtc)
150{
151    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
152    struct radeon_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, &radeon_drm_vblank_deferred, list) {
162	if (e->crtc != crtc)
163	    continue;
164
165	xorg_list_del(&e->list);
166	xorg_list_append(&e->list, &radeon_drm_vblank_signalled);
167    }
168
169    radeon_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
177radeon_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client,
178		       uint64_t id, void *data,
179		       radeon_drm_handler_proc handler,
180		       radeon_drm_abort_proc abort,
181		       Bool is_flip)
182{
183    struct radeon_drm_queue_entry *e;
184
185    e = calloc(1, sizeof(struct radeon_drm_queue_entry));
186    if (!e)
187	return RADEON_DRM_QUEUE_ERROR;
188
189    if (_X_UNLIKELY(radeon_drm_queue_seq == RADEON_DRM_QUEUE_ERROR))
190	radeon_drm_queue_seq++;
191
192    e->seq = radeon_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, &radeon_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
214radeon_drm_abort_client(ClientPtr client)
215{
216    struct radeon_drm_queue_entry *e;
217
218    xorg_list_for_each_entry(e, &radeon_drm_queue, list) {
219	if (e->client == client)
220	    e->handler = NULL;
221    }
222}
223
224/*
225 * Abort specific drm queue entry
226 */
227void
228radeon_drm_abort_entry(uintptr_t seq)
229{
230    struct radeon_drm_queue_entry *e, *tmp;
231
232    if (seq == RADEON_DRM_QUEUE_ERROR)
233	return;
234
235    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) {
236	if (e->seq == seq) {
237	    radeon_drm_abort_one(e);
238	    return;
239	}
240    }
241
242    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_deferred, list) {
243	if (e->seq == seq) {
244	    radeon_drm_abort_one(e);
245	    return;
246	}
247    }
248
249    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
250	if (e->seq == seq) {
251	    radeon_drm_abort_one(e);
252	    break;
253	}
254    }
255}
256
257/*
258 * Abort specific drm queue entry by ID
259 */
260void
261radeon_drm_abort_id(uint64_t id)
262{
263    struct radeon_drm_queue_entry *e, *tmp;
264
265    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
266	if (e->id == id) {
267	    radeon_drm_abort_one(e);
268	    break;
269	}
270    }
271}
272
273/*
274 * drmHandleEvent wrapper
275 */
276int
277radeon_drm_handle_event(int fd, drmEventContext *event_context)
278{
279    struct radeon_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(&radeon_drm_flip_signalled)) {
298	e = xorg_list_first_entry(&radeon_drm_flip_signalled,
299				  struct radeon_drm_queue_entry, list);
300	radeon_drm_queue_handle_one(e);
301    }
302
303    radeon_drm_handle_vblank_signalled();
304
305    return r;
306}
307
308/*
309 * Wait for pending page flip on given CRTC to complete
310 */
311void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc)
312{
313    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
314    RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn);
315    struct radeon_drm_queue_entry *e;
316
317    drmmode_crtc->wait_flip_nesting_level++;
318
319    while (drmmode_crtc->flip_pending &&
320	   !xorg_list_is_empty(&radeon_drm_flip_signalled)) {
321	e = xorg_list_first_entry(&radeon_drm_flip_signalled,
322				  struct radeon_drm_queue_entry, list);
323	radeon_drm_queue_handle_one(e);
324    }
325
326    while (drmmode_crtc->flip_pending
327	   && radeon_drm_handle_event(pRADEONEnt->fd,
328				      &drmmode_crtc->drmmode->event_context) >= 0);
329}
330
331/*
332 * Initialize the DRM event queue
333 */
334void
335radeon_drm_queue_init(ScrnInfoPtr scrn)
336{
337    RADEONInfoPtr info = RADEONPTR(scrn);
338    drmmode_ptr drmmode = &info->drmmode;
339
340    drmmode->event_context.version = 2;
341    drmmode->event_context.vblank_handler = radeon_drm_queue_handler;
342    drmmode->event_context.page_flip_handler = radeon_drm_queue_handler;
343
344    if (radeon_drm_queue_refcnt++)
345	return;
346
347    xorg_list_init(&radeon_drm_queue);
348    xorg_list_init(&radeon_drm_flip_signalled);
349    xorg_list_init(&radeon_drm_vblank_signalled);
350    xorg_list_init(&radeon_drm_vblank_deferred);
351}
352
353/*
354 * Deinitialize the DRM event queue
355 */
356void
357radeon_drm_queue_close(ScrnInfoPtr scrn)
358{
359    struct radeon_drm_queue_entry *e, *tmp;
360
361    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
362	if (e->crtc->scrn == scrn)
363	    radeon_drm_abort_one(e);
364    }
365
366    radeon_drm_queue_refcnt--;
367}
368