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