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