radeon_drm_queue.c revision 39413783
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 <xorg-server.h>
34#include <X11/Xdefs.h>
35#include <list.h>
36
37#include "radeon.h"
38#include "radeon_drm_queue.h"
39
40
41struct radeon_drm_queue_entry {
42    struct xorg_list list;
43    uint64_t usec;
44    uint64_t id;
45    uintptr_t seq;
46    void *data;
47    ClientPtr client;
48    xf86CrtcPtr crtc;
49    radeon_drm_handler_proc handler;
50    radeon_drm_abort_proc abort;
51    unsigned int frame;
52};
53
54static int radeon_drm_queue_refcnt;
55static struct xorg_list radeon_drm_queue;
56static struct xorg_list radeon_drm_flip_signalled;
57static struct xorg_list radeon_drm_vblank_signalled;
58static uintptr_t radeon_drm_queue_seq;
59
60
61/*
62 * Process a DRM event
63 */
64static void
65radeon_drm_queue_handle_one(struct radeon_drm_queue_entry *e)
66{
67    xorg_list_del(&e->list);
68    if (e->handler) {
69	e->handler(e->crtc, e->frame, e->usec, e->data);
70    } else
71	e->abort(e->crtc, e->data);
72    free(e);
73}
74
75static void
76radeon_drm_queue_handler(struct xorg_list *signalled, unsigned int frame,
77			 unsigned int sec, unsigned int usec, void *user_ptr)
78{
79    uintptr_t seq = (uintptr_t)user_ptr;
80    struct radeon_drm_queue_entry *e, *tmp;
81
82    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
83	if (e->seq == seq) {
84	    if (!e->handler) {
85		radeon_drm_queue_handle_one(e);
86		break;
87	    }
88
89	    xorg_list_del(&e->list);
90	    e->usec = (uint64_t)sec * 1000000 + usec;
91	    e->frame = frame;
92	    xorg_list_append(&e->list, signalled);
93	    break;
94	}
95    }
96}
97
98/*
99 * Signal a DRM page flip event
100 */
101static void
102radeon_drm_page_flip_handler(int fd, unsigned int frame, unsigned int sec,
103			     unsigned int usec, void *user_ptr)
104{
105    radeon_drm_queue_handler(&radeon_drm_flip_signalled, frame, sec, usec,
106			     user_ptr);
107}
108
109/*
110 * Signal a DRM vblank event
111 */
112static void
113radeon_drm_vblank_handler(int fd, unsigned int frame, unsigned int sec,
114			  unsigned int usec, void *user_ptr)
115{
116    radeon_drm_queue_handler(&radeon_drm_vblank_signalled, frame, sec, usec,
117			     user_ptr);
118}
119
120/*
121 * Handle deferred DRM vblank events
122 *
123 * This function must be called after radeon_drm_wait_pending_flip, once
124 * it's safe to attempt queueing a flip again
125 */
126void
127radeon_drm_queue_handle_deferred(xf86CrtcPtr crtc)
128{
129    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
130    struct radeon_drm_queue_entry *e, *tmp;
131
132    if (drmmode_crtc->wait_flip_nesting_level == 0 ||
133	--drmmode_crtc->wait_flip_nesting_level > 0)
134	return;
135
136    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) {
137	drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private;
138
139	if (drmmode_crtc->wait_flip_nesting_level == 0)
140	    radeon_drm_queue_handle_one(e);
141    }
142}
143
144/*
145 * Enqueue a potential drm response; when the associated response
146 * appears, we've got data to pass to the handler from here
147 */
148uintptr_t
149radeon_drm_queue_alloc(xf86CrtcPtr crtc, ClientPtr client,
150		       uint64_t id, void *data,
151		       radeon_drm_handler_proc handler,
152		       radeon_drm_abort_proc abort)
153{
154    struct radeon_drm_queue_entry *e;
155
156    e = calloc(1, sizeof(struct radeon_drm_queue_entry));
157    if (!e)
158	return RADEON_DRM_QUEUE_ERROR;
159
160    if (_X_UNLIKELY(radeon_drm_queue_seq == RADEON_DRM_QUEUE_ERROR))
161	radeon_drm_queue_seq++;
162
163    e->seq = radeon_drm_queue_seq++;
164    e->client = client;
165    e->crtc = crtc;
166    e->id = id;
167    e->data = data;
168    e->handler = handler;
169    e->abort = abort;
170
171    xorg_list_append(&e->list, &radeon_drm_queue);
172
173    return e->seq;
174}
175
176/*
177 * Abort one queued DRM entry, removing it
178 * from the list, calling the abort function and
179 * freeing the memory
180 */
181static void
182radeon_drm_abort_one(struct radeon_drm_queue_entry *e)
183{
184    xorg_list_del(&e->list);
185    e->abort(e->crtc, e->data);
186    free(e);
187}
188
189/*
190 * Abort drm queue entries for a client
191 *
192 * NOTE: This keeps the entries in the list until the DRM event arrives,
193 * but then it calls the abort functions instead of the handler
194 * functions.
195 */
196void
197radeon_drm_abort_client(ClientPtr client)
198{
199    struct radeon_drm_queue_entry *e;
200
201    xorg_list_for_each_entry(e, &radeon_drm_queue, list) {
202	if (e->client == client)
203	    e->handler = NULL;
204    }
205}
206
207/*
208 * Abort specific drm queue entry
209 */
210void
211radeon_drm_abort_entry(uintptr_t seq)
212{
213    struct radeon_drm_queue_entry *e, *tmp;
214
215    if (seq == RADEON_DRM_QUEUE_ERROR)
216	return;
217
218    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) {
219	if (e->seq == seq) {
220	    radeon_drm_abort_one(e);
221	    return;
222	}
223    }
224
225    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
226	if (e->seq == seq) {
227	    radeon_drm_abort_one(e);
228	    break;
229	}
230    }
231}
232
233/*
234 * Abort specific drm queue entry by ID
235 */
236void
237radeon_drm_abort_id(uint64_t id)
238{
239    struct radeon_drm_queue_entry *e, *tmp;
240
241    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
242	if (e->id == id) {
243	    radeon_drm_abort_one(e);
244	    break;
245	}
246    }
247}
248
249/*
250 * drmHandleEvent wrapper
251 */
252int
253radeon_drm_handle_event(int fd, drmEventContext *event_context)
254{
255    struct radeon_drm_queue_entry *e, *tmp;
256    int r;
257
258    r = drmHandleEvent(fd, event_context);
259
260    while (!xorg_list_is_empty(&radeon_drm_flip_signalled)) {
261	e = xorg_list_first_entry(&radeon_drm_flip_signalled,
262				  struct radeon_drm_queue_entry, list);
263	radeon_drm_queue_handle_one(e);
264    }
265
266    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_vblank_signalled, list) {
267	drmmode_crtc_private_ptr drmmode_crtc = e->crtc->driver_private;
268
269	if (drmmode_crtc->wait_flip_nesting_level == 0)
270	    radeon_drm_queue_handle_one(e);
271    }
272
273    return r;
274}
275
276/*
277 * Wait for pending page flip on given CRTC to complete
278 */
279void radeon_drm_wait_pending_flip(xf86CrtcPtr crtc)
280{
281    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
282    RADEONEntPtr pRADEONEnt = RADEONEntPriv(crtc->scrn);
283    struct radeon_drm_queue_entry *e;
284
285    drmmode_crtc->wait_flip_nesting_level++;
286
287    while (drmmode_crtc->flip_pending &&
288	   !xorg_list_is_empty(&radeon_drm_flip_signalled)) {
289	e = xorg_list_first_entry(&radeon_drm_flip_signalled,
290				  struct radeon_drm_queue_entry, list);
291	radeon_drm_queue_handle_one(e);
292    }
293
294    while (drmmode_crtc->flip_pending
295	   && radeon_drm_handle_event(pRADEONEnt->fd,
296					  &drmmode_crtc->drmmode->event_context) > 0);
297}
298
299/*
300 * Initialize the DRM event queue
301 */
302void
303radeon_drm_queue_init(ScrnInfoPtr scrn)
304{
305    RADEONInfoPtr info = RADEONPTR(scrn);
306    drmmode_ptr drmmode = &info->drmmode;
307
308    drmmode->event_context.version = 2;
309    drmmode->event_context.vblank_handler = radeon_drm_vblank_handler;
310    drmmode->event_context.page_flip_handler = radeon_drm_page_flip_handler;
311
312    if (radeon_drm_queue_refcnt++)
313	return;
314
315    xorg_list_init(&radeon_drm_queue);
316    xorg_list_init(&radeon_drm_flip_signalled);
317    xorg_list_init(&radeon_drm_vblank_signalled);
318}
319
320/*
321 * Deinitialize the DRM event queue
322 */
323void
324radeon_drm_queue_close(ScrnInfoPtr scrn)
325{
326    struct radeon_drm_queue_entry *e, *tmp;
327
328    xorg_list_for_each_entry_safe(e, tmp, &radeon_drm_queue, list) {
329	if (e->crtc->scrn == scrn)
330	    radeon_drm_abort_one(e);
331    }
332
333    radeon_drm_queue_refcnt--;
334}
335