135c4bbdfSmrg/* 235c4bbdfSmrg * Copyright © 2013 Intel Corporation 335c4bbdfSmrg * Copyright © 2014 Broadcom 435c4bbdfSmrg * 535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person obtaining a 635c4bbdfSmrg * copy of this software and associated documentation files (the "Software"), 735c4bbdfSmrg * to deal in the Software without restriction, including without limitation 835c4bbdfSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 935c4bbdfSmrg * and/or sell copies of the Software, and to permit persons to whom the 1035c4bbdfSmrg * Software is furnished to do so, subject to the following conditions: 1135c4bbdfSmrg * 1235c4bbdfSmrg * The above copyright notice and this permission notice (including the next 1335c4bbdfSmrg * paragraph) shall be included in all copies or substantial portions of the 1435c4bbdfSmrg * Software. 1535c4bbdfSmrg * 1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1735c4bbdfSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1835c4bbdfSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1935c4bbdfSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2035c4bbdfSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2135c4bbdfSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2235c4bbdfSmrg * IN THE SOFTWARE. 2335c4bbdfSmrg */ 2435c4bbdfSmrg 2535c4bbdfSmrg/** 2635c4bbdfSmrg * @file dri2.c 2735c4bbdfSmrg * 2835c4bbdfSmrg * Implements generic support for DRI2 on KMS, using glamor pixmaps 2935c4bbdfSmrg * for color buffer management (no support for other aux buffers), and 3035c4bbdfSmrg * the DRM vblank ioctls. 3135c4bbdfSmrg * 3235c4bbdfSmrg * This doesn't implement pageflipping yet. 3335c4bbdfSmrg */ 3435c4bbdfSmrg 3535c4bbdfSmrg#ifdef HAVE_DIX_CONFIG_H 3635c4bbdfSmrg#include "dix-config.h" 3735c4bbdfSmrg#endif 3835c4bbdfSmrg 3935c4bbdfSmrg#include <time.h> 4035c4bbdfSmrg#include "list.h" 4135c4bbdfSmrg#include "xf86.h" 4235c4bbdfSmrg#include "driver.h" 4335c4bbdfSmrg#include "dri2.h" 4435c4bbdfSmrg 451b5d61b8Smrg#ifdef GLAMOR_HAS_GBM 4635c4bbdfSmrg 4735c4bbdfSmrgenum ms_dri2_frame_event_type { 4835c4bbdfSmrg MS_DRI2_QUEUE_SWAP, 491b5d61b8Smrg MS_DRI2_QUEUE_FLIP, 5035c4bbdfSmrg MS_DRI2_WAIT_MSC, 5135c4bbdfSmrg}; 5235c4bbdfSmrg 5335c4bbdfSmrgtypedef struct ms_dri2_frame_event { 5435c4bbdfSmrg ScreenPtr screen; 5535c4bbdfSmrg 5635c4bbdfSmrg DrawablePtr drawable; 5735c4bbdfSmrg ClientPtr client; 5835c4bbdfSmrg enum ms_dri2_frame_event_type type; 5935c4bbdfSmrg int frame; 6035c4bbdfSmrg xf86CrtcPtr crtc; 6135c4bbdfSmrg 6235c4bbdfSmrg struct xorg_list drawable_resource, client_resource; 6335c4bbdfSmrg 6435c4bbdfSmrg /* for swaps & flips only */ 6535c4bbdfSmrg DRI2SwapEventPtr event_complete; 6635c4bbdfSmrg void *event_data; 6735c4bbdfSmrg DRI2BufferPtr front; 6835c4bbdfSmrg DRI2BufferPtr back; 6935c4bbdfSmrg} ms_dri2_frame_event_rec, *ms_dri2_frame_event_ptr; 7035c4bbdfSmrg 7135c4bbdfSmrgtypedef struct { 7235c4bbdfSmrg int refcnt; 7335c4bbdfSmrg PixmapPtr pixmap; 7435c4bbdfSmrg} ms_dri2_buffer_private_rec, *ms_dri2_buffer_private_ptr; 7535c4bbdfSmrg 7635c4bbdfSmrgstatic DevPrivateKeyRec ms_dri2_client_key; 7735c4bbdfSmrgstatic RESTYPE frame_event_client_type, frame_event_drawable_type; 7835c4bbdfSmrgstatic int ms_dri2_server_generation; 7935c4bbdfSmrg 8035c4bbdfSmrgstruct ms_dri2_resource { 8135c4bbdfSmrg XID id; 8235c4bbdfSmrg RESTYPE type; 8335c4bbdfSmrg struct xorg_list list; 8435c4bbdfSmrg}; 8535c4bbdfSmrg 8635c4bbdfSmrgstatic struct ms_dri2_resource * 8735c4bbdfSmrgms_get_resource(XID id, RESTYPE type) 8835c4bbdfSmrg{ 8935c4bbdfSmrg struct ms_dri2_resource *resource; 9035c4bbdfSmrg void *ptr; 9135c4bbdfSmrg 9235c4bbdfSmrg ptr = NULL; 9335c4bbdfSmrg dixLookupResourceByType(&ptr, id, type, NULL, DixWriteAccess); 9435c4bbdfSmrg if (ptr) 9535c4bbdfSmrg return ptr; 9635c4bbdfSmrg 9735c4bbdfSmrg resource = malloc(sizeof(*resource)); 9835c4bbdfSmrg if (resource == NULL) 9935c4bbdfSmrg return NULL; 10035c4bbdfSmrg 10135c4bbdfSmrg if (!AddResource(id, type, resource)) 10235c4bbdfSmrg return NULL; 10335c4bbdfSmrg 10435c4bbdfSmrg resource->id = id; 10535c4bbdfSmrg resource->type = type; 10635c4bbdfSmrg xorg_list_init(&resource->list); 10735c4bbdfSmrg return resource; 10835c4bbdfSmrg} 10935c4bbdfSmrg 11035c4bbdfSmrgstatic inline PixmapPtr 11135c4bbdfSmrgget_drawable_pixmap(DrawablePtr drawable) 11235c4bbdfSmrg{ 11335c4bbdfSmrg ScreenPtr screen = drawable->pScreen; 11435c4bbdfSmrg 11535c4bbdfSmrg if (drawable->type == DRAWABLE_PIXMAP) 11635c4bbdfSmrg return (PixmapPtr) drawable; 11735c4bbdfSmrg else 11835c4bbdfSmrg return screen->GetWindowPixmap((WindowPtr) drawable); 11935c4bbdfSmrg} 12035c4bbdfSmrg 12135c4bbdfSmrgstatic DRI2Buffer2Ptr 1221b5d61b8Smrgms_dri2_create_buffer2(ScreenPtr screen, DrawablePtr drawable, 1231b5d61b8Smrg unsigned int attachment, unsigned int format) 12435c4bbdfSmrg{ 12535c4bbdfSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 126ed6184dfSmrg modesettingPtr ms = modesettingPTR(scrn); 12735c4bbdfSmrg DRI2Buffer2Ptr buffer; 12835c4bbdfSmrg PixmapPtr pixmap; 12935c4bbdfSmrg CARD32 size; 13035c4bbdfSmrg CARD16 pitch; 13135c4bbdfSmrg ms_dri2_buffer_private_ptr private; 13235c4bbdfSmrg 13335c4bbdfSmrg buffer = calloc(1, sizeof *buffer); 13435c4bbdfSmrg if (buffer == NULL) 13535c4bbdfSmrg return NULL; 13635c4bbdfSmrg 13735c4bbdfSmrg private = calloc(1, sizeof(*private)); 13835c4bbdfSmrg if (private == NULL) { 13935c4bbdfSmrg free(buffer); 14035c4bbdfSmrg return NULL; 14135c4bbdfSmrg } 14235c4bbdfSmrg 14335c4bbdfSmrg pixmap = NULL; 1441b5d61b8Smrg if (attachment == DRI2BufferFrontLeft) { 1451b5d61b8Smrg pixmap = get_drawable_pixmap(drawable); 1461b5d61b8Smrg if (pixmap && pixmap->drawable.pScreen != screen) 1471b5d61b8Smrg pixmap = NULL; 1481b5d61b8Smrg if (pixmap) 1491b5d61b8Smrg pixmap->refcnt++; 1501b5d61b8Smrg } 15135c4bbdfSmrg 15235c4bbdfSmrg if (pixmap == NULL) { 15335c4bbdfSmrg int pixmap_width = drawable->width; 15435c4bbdfSmrg int pixmap_height = drawable->height; 15535c4bbdfSmrg int pixmap_cpp = (format != 0) ? format : drawable->depth; 15635c4bbdfSmrg 15735c4bbdfSmrg /* Assume that non-color-buffers require special 15835c4bbdfSmrg * device-specific handling. Mesa currently makes no requests 15935c4bbdfSmrg * for non-color aux buffers. 16035c4bbdfSmrg */ 16135c4bbdfSmrg switch (attachment) { 16235c4bbdfSmrg case DRI2BufferAccum: 16335c4bbdfSmrg case DRI2BufferBackLeft: 16435c4bbdfSmrg case DRI2BufferBackRight: 16535c4bbdfSmrg case DRI2BufferFakeFrontLeft: 16635c4bbdfSmrg case DRI2BufferFakeFrontRight: 16735c4bbdfSmrg case DRI2BufferFrontLeft: 16835c4bbdfSmrg case DRI2BufferFrontRight: 16935c4bbdfSmrg break; 17035c4bbdfSmrg 17135c4bbdfSmrg case DRI2BufferStencil: 17235c4bbdfSmrg case DRI2BufferDepth: 17335c4bbdfSmrg case DRI2BufferDepthStencil: 17435c4bbdfSmrg case DRI2BufferHiz: 17535c4bbdfSmrg default: 17635c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 17735c4bbdfSmrg "Request for DRI2 buffer attachment %d unsupported\n", 17835c4bbdfSmrg attachment); 17935c4bbdfSmrg free(private); 18035c4bbdfSmrg free(buffer); 18135c4bbdfSmrg return NULL; 18235c4bbdfSmrg } 18335c4bbdfSmrg 18435c4bbdfSmrg pixmap = screen->CreatePixmap(screen, 18535c4bbdfSmrg pixmap_width, 18635c4bbdfSmrg pixmap_height, 18735c4bbdfSmrg pixmap_cpp, 18835c4bbdfSmrg 0); 18935c4bbdfSmrg if (pixmap == NULL) { 19035c4bbdfSmrg free(private); 19135c4bbdfSmrg free(buffer); 19235c4bbdfSmrg return NULL; 19335c4bbdfSmrg } 19435c4bbdfSmrg } 19535c4bbdfSmrg 19635c4bbdfSmrg buffer->attachment = attachment; 19735c4bbdfSmrg buffer->cpp = pixmap->drawable.bitsPerPixel / 8; 19835c4bbdfSmrg buffer->format = format; 19935c4bbdfSmrg /* The buffer's flags field is unused by the client drivers in 20035c4bbdfSmrg * Mesa currently. 20135c4bbdfSmrg */ 20235c4bbdfSmrg buffer->flags = 0; 20335c4bbdfSmrg 204ed6184dfSmrg buffer->name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size); 20535c4bbdfSmrg buffer->pitch = pitch; 20635c4bbdfSmrg if (buffer->name == -1) { 20735c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_ERROR, 20835c4bbdfSmrg "Failed to get DRI2 name for pixmap\n"); 20935c4bbdfSmrg screen->DestroyPixmap(pixmap); 21035c4bbdfSmrg free(private); 21135c4bbdfSmrg free(buffer); 21235c4bbdfSmrg return NULL; 21335c4bbdfSmrg } 21435c4bbdfSmrg 21535c4bbdfSmrg buffer->driverPrivate = private; 21635c4bbdfSmrg private->refcnt = 1; 21735c4bbdfSmrg private->pixmap = pixmap; 21835c4bbdfSmrg 21935c4bbdfSmrg return buffer; 22035c4bbdfSmrg} 22135c4bbdfSmrg 2221b5d61b8Smrgstatic DRI2Buffer2Ptr 2231b5d61b8Smrgms_dri2_create_buffer(DrawablePtr drawable, unsigned int attachment, 2241b5d61b8Smrg unsigned int format) 2251b5d61b8Smrg{ 2261b5d61b8Smrg return ms_dri2_create_buffer2(drawable->pScreen, drawable, attachment, 2271b5d61b8Smrg format); 2281b5d61b8Smrg} 2291b5d61b8Smrg 23035c4bbdfSmrgstatic void 23135c4bbdfSmrgms_dri2_reference_buffer(DRI2Buffer2Ptr buffer) 23235c4bbdfSmrg{ 23335c4bbdfSmrg if (buffer) { 23435c4bbdfSmrg ms_dri2_buffer_private_ptr private = buffer->driverPrivate; 23535c4bbdfSmrg private->refcnt++; 23635c4bbdfSmrg } 23735c4bbdfSmrg} 23835c4bbdfSmrg 2391b5d61b8Smrgstatic void ms_dri2_destroy_buffer2(ScreenPtr unused, DrawablePtr unused2, 2401b5d61b8Smrg DRI2Buffer2Ptr buffer) 24135c4bbdfSmrg{ 24235c4bbdfSmrg if (!buffer) 24335c4bbdfSmrg return; 24435c4bbdfSmrg 24535c4bbdfSmrg if (buffer->driverPrivate) { 24635c4bbdfSmrg ms_dri2_buffer_private_ptr private = buffer->driverPrivate; 24735c4bbdfSmrg if (--private->refcnt == 0) { 24835c4bbdfSmrg ScreenPtr screen = private->pixmap->drawable.pScreen; 24935c4bbdfSmrg screen->DestroyPixmap(private->pixmap); 25035c4bbdfSmrg free(private); 25135c4bbdfSmrg free(buffer); 25235c4bbdfSmrg } 25335c4bbdfSmrg } else { 25435c4bbdfSmrg free(buffer); 25535c4bbdfSmrg } 25635c4bbdfSmrg} 25735c4bbdfSmrg 2581b5d61b8Smrgstatic void ms_dri2_destroy_buffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer) 2591b5d61b8Smrg{ 2601b5d61b8Smrg ms_dri2_destroy_buffer2(NULL, drawable, buffer); 2611b5d61b8Smrg} 2621b5d61b8Smrg 26335c4bbdfSmrgstatic void 2641b5d61b8Smrgms_dri2_copy_region2(ScreenPtr screen, DrawablePtr drawable, RegionPtr pRegion, 2651b5d61b8Smrg DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer) 26635c4bbdfSmrg{ 26735c4bbdfSmrg ms_dri2_buffer_private_ptr src_priv = sourceBuffer->driverPrivate; 26835c4bbdfSmrg ms_dri2_buffer_private_ptr dst_priv = destBuffer->driverPrivate; 26935c4bbdfSmrg PixmapPtr src_pixmap = src_priv->pixmap; 27035c4bbdfSmrg PixmapPtr dst_pixmap = dst_priv->pixmap; 27135c4bbdfSmrg DrawablePtr src = (sourceBuffer->attachment == DRI2BufferFrontLeft) 27235c4bbdfSmrg ? drawable : &src_pixmap->drawable; 27335c4bbdfSmrg DrawablePtr dst = (destBuffer->attachment == DRI2BufferFrontLeft) 27435c4bbdfSmrg ? drawable : &dst_pixmap->drawable; 2751b5d61b8Smrg int off_x = 0, off_y = 0; 2761b5d61b8Smrg Bool translate = FALSE; 27735c4bbdfSmrg RegionPtr pCopyClip; 27835c4bbdfSmrg GCPtr gc; 27935c4bbdfSmrg 2801b5d61b8Smrg if (destBuffer->attachment == DRI2BufferFrontLeft && 2811b5d61b8Smrg drawable->pScreen != screen) { 2821b5d61b8Smrg dst = DRI2UpdatePrime(drawable, destBuffer); 2831b5d61b8Smrg if (!dst) 2841b5d61b8Smrg return; 2851b5d61b8Smrg if (dst != drawable) 2861b5d61b8Smrg translate = TRUE; 2871b5d61b8Smrg } 2881b5d61b8Smrg 2891b5d61b8Smrg if (translate && drawable->type == DRAWABLE_WINDOW) { 2901b5d61b8Smrg#ifdef COMPOSITE 2911b5d61b8Smrg PixmapPtr pixmap = get_drawable_pixmap(drawable); 2921b5d61b8Smrg off_x = -pixmap->screen_x; 2931b5d61b8Smrg off_y = -pixmap->screen_y; 2941b5d61b8Smrg#endif 2951b5d61b8Smrg off_x += drawable->x; 2961b5d61b8Smrg off_y += drawable->y; 2971b5d61b8Smrg } 2981b5d61b8Smrg 29935c4bbdfSmrg gc = GetScratchGC(dst->depth, screen); 30035c4bbdfSmrg if (!gc) 30135c4bbdfSmrg return; 30235c4bbdfSmrg 30335c4bbdfSmrg pCopyClip = REGION_CREATE(screen, NULL, 0); 30435c4bbdfSmrg REGION_COPY(screen, pCopyClip, pRegion); 3051b5d61b8Smrg if (translate) 3061b5d61b8Smrg REGION_TRANSLATE(screen, pCopyClip, off_x, off_y); 30735c4bbdfSmrg (*gc->funcs->ChangeClip) (gc, CT_REGION, pCopyClip, 0); 30835c4bbdfSmrg ValidateGC(dst, gc); 30935c4bbdfSmrg 31035c4bbdfSmrg /* It's important that this copy gets submitted before the direct 31135c4bbdfSmrg * rendering client submits rendering for the next frame, but we 31235c4bbdfSmrg * don't actually need to submit right now. The client will wait 31335c4bbdfSmrg * for the DRI2CopyRegion reply or the swap buffer event before 31435c4bbdfSmrg * rendering, and we'll hit the flush callback chain before those 31535c4bbdfSmrg * messages are sent. We submit our batch buffers from the flush 31635c4bbdfSmrg * callback chain so we know that will happen before the client 31735c4bbdfSmrg * tries to render again. 31835c4bbdfSmrg */ 31935c4bbdfSmrg gc->ops->CopyArea(src, dst, gc, 32035c4bbdfSmrg 0, 0, 32135c4bbdfSmrg drawable->width, drawable->height, 3221b5d61b8Smrg off_x, off_y); 32335c4bbdfSmrg 32435c4bbdfSmrg FreeScratchGC(gc); 32535c4bbdfSmrg} 32635c4bbdfSmrg 3271b5d61b8Smrgstatic void 3281b5d61b8Smrgms_dri2_copy_region(DrawablePtr drawable, RegionPtr pRegion, 3291b5d61b8Smrg DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer) 3301b5d61b8Smrg{ 3311b5d61b8Smrg ms_dri2_copy_region2(drawable->pScreen, drawable, pRegion, destBuffer, 3321b5d61b8Smrg sourceBuffer); 3331b5d61b8Smrg} 3341b5d61b8Smrg 33535c4bbdfSmrgstatic uint64_t 33635c4bbdfSmrggettime_us(void) 33735c4bbdfSmrg{ 33835c4bbdfSmrg struct timespec tv; 33935c4bbdfSmrg 34035c4bbdfSmrg if (clock_gettime(CLOCK_MONOTONIC, &tv)) 34135c4bbdfSmrg return 0; 34235c4bbdfSmrg 34335c4bbdfSmrg return (uint64_t)tv.tv_sec * 1000000 + tv.tv_nsec / 1000; 34435c4bbdfSmrg} 34535c4bbdfSmrg 34635c4bbdfSmrg/** 34735c4bbdfSmrg * Get current frame count and frame count timestamp, based on drawable's 34835c4bbdfSmrg * crtc. 34935c4bbdfSmrg */ 35035c4bbdfSmrgstatic int 35135c4bbdfSmrgms_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) 35235c4bbdfSmrg{ 35335c4bbdfSmrg int ret; 35435c4bbdfSmrg xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 35535c4bbdfSmrg 35635c4bbdfSmrg /* Drawable not displayed, make up a *monotonic* value */ 35735c4bbdfSmrg if (crtc == NULL) { 35835c4bbdfSmrg *ust = gettime_us(); 35935c4bbdfSmrg *msc = 0; 36035c4bbdfSmrg return TRUE; 36135c4bbdfSmrg } 36235c4bbdfSmrg 36335c4bbdfSmrg ret = ms_get_crtc_ust_msc(crtc, ust, msc); 36435c4bbdfSmrg 36535c4bbdfSmrg if (ret) 36635c4bbdfSmrg return FALSE; 36735c4bbdfSmrg 36835c4bbdfSmrg return TRUE; 36935c4bbdfSmrg} 37035c4bbdfSmrg 37135c4bbdfSmrgstatic XID 37235c4bbdfSmrgget_client_id(ClientPtr client) 37335c4bbdfSmrg{ 37435c4bbdfSmrg XID *ptr = dixGetPrivateAddr(&client->devPrivates, &ms_dri2_client_key); 37535c4bbdfSmrg if (*ptr == 0) 37635c4bbdfSmrg *ptr = FakeClientID(client->index); 37735c4bbdfSmrg return *ptr; 37835c4bbdfSmrg} 37935c4bbdfSmrg 38035c4bbdfSmrg/* 38135c4bbdfSmrg * Hook this frame event into the server resource 38235c4bbdfSmrg * database so we can clean it up if the drawable or 38335c4bbdfSmrg * client exits while the swap is pending 38435c4bbdfSmrg */ 38535c4bbdfSmrgstatic Bool 38635c4bbdfSmrgms_dri2_add_frame_event(ms_dri2_frame_event_ptr info) 38735c4bbdfSmrg{ 38835c4bbdfSmrg struct ms_dri2_resource *resource; 38935c4bbdfSmrg 39035c4bbdfSmrg resource = ms_get_resource(get_client_id(info->client), 39135c4bbdfSmrg frame_event_client_type); 39235c4bbdfSmrg if (resource == NULL) 39335c4bbdfSmrg return FALSE; 39435c4bbdfSmrg 39535c4bbdfSmrg xorg_list_add(&info->client_resource, &resource->list); 39635c4bbdfSmrg 39735c4bbdfSmrg resource = ms_get_resource(info->drawable->id, frame_event_drawable_type); 39835c4bbdfSmrg if (resource == NULL) { 39935c4bbdfSmrg xorg_list_del(&info->client_resource); 40035c4bbdfSmrg return FALSE; 40135c4bbdfSmrg } 40235c4bbdfSmrg 40335c4bbdfSmrg xorg_list_add(&info->drawable_resource, &resource->list); 40435c4bbdfSmrg 40535c4bbdfSmrg return TRUE; 40635c4bbdfSmrg} 40735c4bbdfSmrg 40835c4bbdfSmrgstatic void 40935c4bbdfSmrgms_dri2_del_frame_event(ms_dri2_frame_event_rec *info) 41035c4bbdfSmrg{ 41135c4bbdfSmrg xorg_list_del(&info->client_resource); 41235c4bbdfSmrg xorg_list_del(&info->drawable_resource); 41335c4bbdfSmrg 41435c4bbdfSmrg if (info->front) 41535c4bbdfSmrg ms_dri2_destroy_buffer(NULL, info->front); 41635c4bbdfSmrg if (info->back) 41735c4bbdfSmrg ms_dri2_destroy_buffer(NULL, info->back); 41835c4bbdfSmrg 41935c4bbdfSmrg free(info); 42035c4bbdfSmrg} 42135c4bbdfSmrg 42235c4bbdfSmrgstatic void 42335c4bbdfSmrgms_dri2_blit_swap(DrawablePtr drawable, 42435c4bbdfSmrg DRI2BufferPtr dst, 42535c4bbdfSmrg DRI2BufferPtr src) 42635c4bbdfSmrg{ 42735c4bbdfSmrg BoxRec box; 42835c4bbdfSmrg RegionRec region; 42935c4bbdfSmrg 43035c4bbdfSmrg box.x1 = 0; 43135c4bbdfSmrg box.y1 = 0; 43235c4bbdfSmrg box.x2 = drawable->width; 43335c4bbdfSmrg box.y2 = drawable->height; 43435c4bbdfSmrg REGION_INIT(pScreen, ®ion, &box, 0); 43535c4bbdfSmrg 43635c4bbdfSmrg ms_dri2_copy_region(drawable, ®ion, dst, src); 43735c4bbdfSmrg} 43835c4bbdfSmrg 4391b5d61b8Smrgstruct ms_dri2_vblank_event { 4401b5d61b8Smrg XID drawable_id; 4411b5d61b8Smrg ClientPtr client; 4421b5d61b8Smrg DRI2SwapEventPtr event_complete; 4431b5d61b8Smrg void *event_data; 4441b5d61b8Smrg}; 4451b5d61b8Smrg 4461b5d61b8Smrgstatic void 4471b5d61b8Smrgms_dri2_flip_abort(modesettingPtr ms, void *data) 4481b5d61b8Smrg{ 4491b5d61b8Smrg struct ms_present_vblank_event *event = data; 4501b5d61b8Smrg 4511b5d61b8Smrg ms->drmmode.dri2_flipping = FALSE; 4521b5d61b8Smrg free(event); 4531b5d61b8Smrg} 4541b5d61b8Smrg 4551b5d61b8Smrgstatic void 4561b5d61b8Smrgms_dri2_flip_handler(modesettingPtr ms, uint64_t msc, 4571b5d61b8Smrg uint64_t ust, void *data) 4581b5d61b8Smrg{ 4591b5d61b8Smrg struct ms_dri2_vblank_event *event = data; 4601b5d61b8Smrg uint32_t frame = msc; 4611b5d61b8Smrg uint32_t tv_sec = ust / 1000000; 4621b5d61b8Smrg uint32_t tv_usec = ust % 1000000; 4631b5d61b8Smrg DrawablePtr drawable; 4641b5d61b8Smrg int status; 4651b5d61b8Smrg 4661b5d61b8Smrg status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, 4671b5d61b8Smrg M_ANY, DixWriteAccess); 4681b5d61b8Smrg if (status == Success) 4691b5d61b8Smrg DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec, 4701b5d61b8Smrg DRI2_FLIP_COMPLETE, event->event_complete, 4711b5d61b8Smrg event->event_data); 4721b5d61b8Smrg 4731b5d61b8Smrg ms->drmmode.dri2_flipping = FALSE; 4741b5d61b8Smrg free(event); 4751b5d61b8Smrg} 4761b5d61b8Smrg 4771b5d61b8Smrgstatic Bool 4781b5d61b8Smrgms_dri2_schedule_flip(ms_dri2_frame_event_ptr info) 4791b5d61b8Smrg{ 4801b5d61b8Smrg DrawablePtr draw = info->drawable; 4811b5d61b8Smrg ScreenPtr screen = draw->pScreen; 4821b5d61b8Smrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 4831b5d61b8Smrg modesettingPtr ms = modesettingPTR(scrn); 4841b5d61b8Smrg ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate; 4851b5d61b8Smrg struct ms_dri2_vblank_event *event; 4861b5d61b8Smrg drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private; 4871b5d61b8Smrg 4881b5d61b8Smrg event = calloc(1, sizeof(struct ms_dri2_vblank_event)); 4891b5d61b8Smrg if (!event) 4901b5d61b8Smrg return FALSE; 4911b5d61b8Smrg 4921b5d61b8Smrg event->drawable_id = draw->id; 4931b5d61b8Smrg event->client = info->client; 4941b5d61b8Smrg event->event_complete = info->event_complete; 4951b5d61b8Smrg event->event_data = info->event_data; 4961b5d61b8Smrg 4971b5d61b8Smrg if (ms_do_pageflip(screen, back_priv->pixmap, event, 4981b5d61b8Smrg drmmode_crtc->vblank_pipe, FALSE, 4991b5d61b8Smrg ms_dri2_flip_handler, 500ed6184dfSmrg ms_dri2_flip_abort, 501ed6184dfSmrg "DRI2-flip")) { 5021b5d61b8Smrg ms->drmmode.dri2_flipping = TRUE; 5031b5d61b8Smrg return TRUE; 5041b5d61b8Smrg } 5051b5d61b8Smrg return FALSE; 5061b5d61b8Smrg} 5071b5d61b8Smrg 5081b5d61b8Smrgstatic Bool 5091b5d61b8Smrgupdate_front(DrawablePtr draw, DRI2BufferPtr front) 5101b5d61b8Smrg{ 5111b5d61b8Smrg ScreenPtr screen = draw->pScreen; 5121b5d61b8Smrg PixmapPtr pixmap = get_drawable_pixmap(draw); 5131b5d61b8Smrg ms_dri2_buffer_private_ptr priv = front->driverPrivate; 514ed6184dfSmrg modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen)); 5151b5d61b8Smrg CARD32 size; 5161b5d61b8Smrg CARD16 pitch; 5171b5d61b8Smrg int name; 5181b5d61b8Smrg 519ed6184dfSmrg name = ms->glamor.name_from_pixmap(pixmap, &pitch, &size); 5201b5d61b8Smrg if (name < 0) 5211b5d61b8Smrg return FALSE; 5221b5d61b8Smrg 5231b5d61b8Smrg front->name = name; 5241b5d61b8Smrg 5251b5d61b8Smrg (*screen->DestroyPixmap) (priv->pixmap); 5261b5d61b8Smrg front->pitch = pixmap->devKind; 5271b5d61b8Smrg front->cpp = pixmap->drawable.bitsPerPixel / 8; 5281b5d61b8Smrg priv->pixmap = pixmap; 5291b5d61b8Smrg pixmap->refcnt++; 5301b5d61b8Smrg 5311b5d61b8Smrg return TRUE; 5321b5d61b8Smrg} 5331b5d61b8Smrg 5341b5d61b8Smrgstatic Bool 5351b5d61b8Smrgcan_exchange(ScrnInfoPtr scrn, DrawablePtr draw, 5361b5d61b8Smrg DRI2BufferPtr front, DRI2BufferPtr back) 5371b5d61b8Smrg{ 5381b5d61b8Smrg ms_dri2_buffer_private_ptr front_priv = front->driverPrivate; 5391b5d61b8Smrg ms_dri2_buffer_private_ptr back_priv = back->driverPrivate; 5401b5d61b8Smrg PixmapPtr front_pixmap; 5411b5d61b8Smrg PixmapPtr back_pixmap = back_priv->pixmap; 5421b5d61b8Smrg xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); 5431b5d61b8Smrg int num_crtcs_on = 0; 5441b5d61b8Smrg int i; 5451b5d61b8Smrg 5461b5d61b8Smrg for (i = 0; i < config->num_crtc; i++) { 5471b5d61b8Smrg drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private; 5481b5d61b8Smrg 5491b5d61b8Smrg /* Don't do pageflipping if CRTCs are rotated. */ 5501b5d61b8Smrg#ifdef GLAMOR_HAS_GBM 5511b5d61b8Smrg if (drmmode_crtc->rotate_bo.gbm) 5521b5d61b8Smrg return FALSE; 5531b5d61b8Smrg#endif 5541b5d61b8Smrg 555a035e2b2Smrg if (xf86_crtc_on(config->crtc[i])) 5561b5d61b8Smrg num_crtcs_on++; 5571b5d61b8Smrg } 5581b5d61b8Smrg 5591b5d61b8Smrg /* We can't do pageflipping if all the CRTCs are off. */ 5601b5d61b8Smrg if (num_crtcs_on == 0) 5611b5d61b8Smrg return FALSE; 5621b5d61b8Smrg 5631b5d61b8Smrg if (!update_front(draw, front)) 5641b5d61b8Smrg return FALSE; 5651b5d61b8Smrg 5661b5d61b8Smrg front_pixmap = front_priv->pixmap; 5671b5d61b8Smrg 5681b5d61b8Smrg if (front_pixmap->drawable.width != back_pixmap->drawable.width) 5691b5d61b8Smrg return FALSE; 5701b5d61b8Smrg 5711b5d61b8Smrg if (front_pixmap->drawable.height != back_pixmap->drawable.height) 5721b5d61b8Smrg return FALSE; 5731b5d61b8Smrg 5741b5d61b8Smrg if (front_pixmap->drawable.bitsPerPixel != 5751b5d61b8Smrg back_pixmap->drawable.bitsPerPixel) 5761b5d61b8Smrg return FALSE; 5771b5d61b8Smrg 5781b5d61b8Smrg if (front_pixmap->devKind != back_pixmap->devKind) 5791b5d61b8Smrg return FALSE; 5801b5d61b8Smrg 5811b5d61b8Smrg return TRUE; 5821b5d61b8Smrg} 5831b5d61b8Smrg 5841b5d61b8Smrgstatic Bool 5851b5d61b8Smrgcan_flip(ScrnInfoPtr scrn, DrawablePtr draw, 5861b5d61b8Smrg DRI2BufferPtr front, DRI2BufferPtr back) 5871b5d61b8Smrg{ 5881b5d61b8Smrg modesettingPtr ms = modesettingPTR(scrn); 5891b5d61b8Smrg 5901b5d61b8Smrg return draw->type == DRAWABLE_WINDOW && 5911b5d61b8Smrg ms->drmmode.pageflip && 5925a7dfde8Smrg !ms->drmmode.sprites_visible && 5931b5d61b8Smrg !ms->drmmode.present_flipping && 5941b5d61b8Smrg scrn->vtSema && 5951b5d61b8Smrg DRI2CanFlip(draw) && can_exchange(scrn, draw, front, back); 5961b5d61b8Smrg} 5971b5d61b8Smrg 5981b5d61b8Smrgstatic void 5991b5d61b8Smrgms_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, 6001b5d61b8Smrg DRI2BufferPtr back) 6011b5d61b8Smrg{ 6021b5d61b8Smrg ms_dri2_buffer_private_ptr front_priv = front->driverPrivate; 6031b5d61b8Smrg ms_dri2_buffer_private_ptr back_priv = back->driverPrivate; 6041b5d61b8Smrg ScreenPtr screen = draw->pScreen; 6051b5d61b8Smrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 6061b5d61b8Smrg modesettingPtr ms = modesettingPTR(scrn); 6071b5d61b8Smrg msPixmapPrivPtr front_pix = msGetPixmapPriv(&ms->drmmode, front_priv->pixmap); 6081b5d61b8Smrg msPixmapPrivPtr back_pix = msGetPixmapPriv(&ms->drmmode, back_priv->pixmap); 6091b5d61b8Smrg msPixmapPrivRec tmp_pix; 6101b5d61b8Smrg RegionRec region; 6111b5d61b8Smrg int tmp; 6121b5d61b8Smrg 6131b5d61b8Smrg /* Swap BO names so DRI works */ 6141b5d61b8Smrg tmp = front->name; 6151b5d61b8Smrg front->name = back->name; 6161b5d61b8Smrg back->name = tmp; 6171b5d61b8Smrg 6181b5d61b8Smrg /* Swap pixmap privates */ 6191b5d61b8Smrg tmp_pix = *front_pix; 6201b5d61b8Smrg *front_pix = *back_pix; 6211b5d61b8Smrg *back_pix = tmp_pix; 6221b5d61b8Smrg 623ed6184dfSmrg ms->glamor.egl_exchange_buffers(front_priv->pixmap, back_priv->pixmap); 6241b5d61b8Smrg 6251b5d61b8Smrg /* Post damage on the front buffer so that listeners, such 6261b5d61b8Smrg * as DisplayLink know take a copy and shove it over the USB. 6271b5d61b8Smrg */ 6281b5d61b8Smrg region.extents.x1 = region.extents.y1 = 0; 6291b5d61b8Smrg region.extents.x2 = front_priv->pixmap->drawable.width; 6301b5d61b8Smrg region.extents.y2 = front_priv->pixmap->drawable.height; 6311b5d61b8Smrg region.data = NULL; 6321b5d61b8Smrg DamageRegionAppend(&front_priv->pixmap->drawable, ®ion); 6331b5d61b8Smrg DamageRegionProcessPending(&front_priv->pixmap->drawable); 6341b5d61b8Smrg} 6351b5d61b8Smrg 63635c4bbdfSmrgstatic void 63735c4bbdfSmrgms_dri2_frame_event_handler(uint64_t msc, 63835c4bbdfSmrg uint64_t usec, 63935c4bbdfSmrg void *data) 64035c4bbdfSmrg{ 64135c4bbdfSmrg ms_dri2_frame_event_ptr frame_info = data; 64235c4bbdfSmrg DrawablePtr drawable = frame_info->drawable; 64335c4bbdfSmrg ScreenPtr screen = frame_info->screen; 64435c4bbdfSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 64535c4bbdfSmrg uint32_t tv_sec = usec / 1000000; 64635c4bbdfSmrg uint32_t tv_usec = usec % 1000000; 64735c4bbdfSmrg 64835c4bbdfSmrg if (!drawable) { 64935c4bbdfSmrg ms_dri2_del_frame_event(frame_info); 65035c4bbdfSmrg return; 65135c4bbdfSmrg } 65235c4bbdfSmrg 65335c4bbdfSmrg switch (frame_info->type) { 6541b5d61b8Smrg case MS_DRI2_QUEUE_FLIP: 6551b5d61b8Smrg if (can_flip(scrn, drawable, frame_info->front, frame_info->back) && 6561b5d61b8Smrg ms_dri2_schedule_flip(frame_info)) { 6571b5d61b8Smrg ms_dri2_exchange_buffers(drawable, frame_info->front, frame_info->back); 6581b5d61b8Smrg break; 6591b5d61b8Smrg } 6601b5d61b8Smrg /* else fall through to blit */ 66135c4bbdfSmrg case MS_DRI2_QUEUE_SWAP: 66235c4bbdfSmrg ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back); 66335c4bbdfSmrg DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec, 66435c4bbdfSmrg DRI2_BLIT_COMPLETE, 66535c4bbdfSmrg frame_info->client ? frame_info->event_complete : NULL, 66635c4bbdfSmrg frame_info->event_data); 66735c4bbdfSmrg break; 66835c4bbdfSmrg 66935c4bbdfSmrg case MS_DRI2_WAIT_MSC: 67035c4bbdfSmrg if (frame_info->client) 67135c4bbdfSmrg DRI2WaitMSCComplete(frame_info->client, drawable, 67235c4bbdfSmrg msc, tv_sec, tv_usec); 67335c4bbdfSmrg break; 67435c4bbdfSmrg 67535c4bbdfSmrg default: 67635c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 67735c4bbdfSmrg "%s: unknown vblank event (type %d) received\n", __func__, 67835c4bbdfSmrg frame_info->type); 67935c4bbdfSmrg break; 68035c4bbdfSmrg } 68135c4bbdfSmrg 68235c4bbdfSmrg ms_dri2_del_frame_event(frame_info); 68335c4bbdfSmrg} 68435c4bbdfSmrg 68535c4bbdfSmrgstatic void 68635c4bbdfSmrgms_dri2_frame_event_abort(void *data) 68735c4bbdfSmrg{ 68835c4bbdfSmrg ms_dri2_frame_event_ptr frame_info = data; 68935c4bbdfSmrg 69035c4bbdfSmrg ms_dri2_del_frame_event(frame_info); 69135c4bbdfSmrg} 69235c4bbdfSmrg 69335c4bbdfSmrg/** 69435c4bbdfSmrg * Request a DRM event when the requested conditions will be satisfied. 69535c4bbdfSmrg * 69635c4bbdfSmrg * We need to handle the event and ask the server to wake up the client when 69735c4bbdfSmrg * we receive it. 69835c4bbdfSmrg */ 69935c4bbdfSmrgstatic int 70035c4bbdfSmrgms_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc, 70135c4bbdfSmrg CARD64 divisor, CARD64 remainder) 70235c4bbdfSmrg{ 70335c4bbdfSmrg ScreenPtr screen = draw->pScreen; 70435c4bbdfSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 70535c4bbdfSmrg ms_dri2_frame_event_ptr wait_info; 70635c4bbdfSmrg int ret; 70735c4bbdfSmrg xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 70835c4bbdfSmrg CARD64 current_msc, current_ust, request_msc; 70935c4bbdfSmrg uint32_t seq; 7101b5d61b8Smrg uint64_t queued_msc; 71135c4bbdfSmrg 71235c4bbdfSmrg /* Drawable not visible, return immediately */ 71335c4bbdfSmrg if (!crtc) 71435c4bbdfSmrg goto out_complete; 71535c4bbdfSmrg 71635c4bbdfSmrg wait_info = calloc(1, sizeof(*wait_info)); 71735c4bbdfSmrg if (!wait_info) 71835c4bbdfSmrg goto out_complete; 71935c4bbdfSmrg 72035c4bbdfSmrg wait_info->screen = screen; 72135c4bbdfSmrg wait_info->drawable = draw; 72235c4bbdfSmrg wait_info->client = client; 72335c4bbdfSmrg wait_info->type = MS_DRI2_WAIT_MSC; 72435c4bbdfSmrg 72535c4bbdfSmrg if (!ms_dri2_add_frame_event(wait_info)) { 72635c4bbdfSmrg free(wait_info); 72735c4bbdfSmrg wait_info = NULL; 72835c4bbdfSmrg goto out_complete; 72935c4bbdfSmrg } 73035c4bbdfSmrg 73135c4bbdfSmrg /* Get current count */ 73235c4bbdfSmrg ret = ms_get_crtc_ust_msc(crtc, ¤t_ust, ¤t_msc); 73335c4bbdfSmrg 73435c4bbdfSmrg /* 73535c4bbdfSmrg * If divisor is zero, or current_msc is smaller than target_msc, 73635c4bbdfSmrg * we just need to make sure target_msc passes before waking up the 73735c4bbdfSmrg * client. 73835c4bbdfSmrg */ 73935c4bbdfSmrg if (divisor == 0 || current_msc < target_msc) { 74035c4bbdfSmrg /* If target_msc already reached or passed, set it to 74135c4bbdfSmrg * current_msc to ensure we return a reasonable value back 74235c4bbdfSmrg * to the caller. This keeps the client from continually 74335c4bbdfSmrg * sending us MSC targets from the past by forcibly updating 74435c4bbdfSmrg * their count on this call. 74535c4bbdfSmrg */ 74635c4bbdfSmrg seq = ms_drm_queue_alloc(crtc, wait_info, 74735c4bbdfSmrg ms_dri2_frame_event_handler, 74835c4bbdfSmrg ms_dri2_frame_event_abort); 74935c4bbdfSmrg if (!seq) 75035c4bbdfSmrg goto out_free; 75135c4bbdfSmrg 75235c4bbdfSmrg if (current_msc >= target_msc) 75335c4bbdfSmrg target_msc = current_msc; 7541b5d61b8Smrg 7551b5d61b8Smrg ret = ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, target_msc, &queued_msc, seq); 7561b5d61b8Smrg if (!ret) { 75735c4bbdfSmrg static int limit = 5; 75835c4bbdfSmrg if (limit) { 75935c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 76035c4bbdfSmrg "%s:%d get vblank counter failed: %s\n", 76135c4bbdfSmrg __FUNCTION__, __LINE__, 76235c4bbdfSmrg strerror(errno)); 76335c4bbdfSmrg limit--; 76435c4bbdfSmrg } 76535c4bbdfSmrg goto out_free; 76635c4bbdfSmrg } 76735c4bbdfSmrg 7681b5d61b8Smrg wait_info->frame = queued_msc; 76935c4bbdfSmrg DRI2BlockClient(client, draw); 77035c4bbdfSmrg return TRUE; 77135c4bbdfSmrg } 77235c4bbdfSmrg 77335c4bbdfSmrg /* 77435c4bbdfSmrg * If we get here, target_msc has already passed or we don't have one, 77535c4bbdfSmrg * so we queue an event that will satisfy the divisor/remainder equation. 77635c4bbdfSmrg */ 77735c4bbdfSmrg request_msc = current_msc - (current_msc % divisor) + 77835c4bbdfSmrg remainder; 77935c4bbdfSmrg /* 78035c4bbdfSmrg * If calculated remainder is larger than requested remainder, 78135c4bbdfSmrg * it means we've passed the last point where 78235c4bbdfSmrg * seq % divisor == remainder, so we need to wait for the next time 78335c4bbdfSmrg * that will happen. 78435c4bbdfSmrg */ 78535c4bbdfSmrg if ((current_msc % divisor) >= remainder) 78635c4bbdfSmrg request_msc += divisor; 78735c4bbdfSmrg 78835c4bbdfSmrg seq = ms_drm_queue_alloc(crtc, wait_info, 78935c4bbdfSmrg ms_dri2_frame_event_handler, 79035c4bbdfSmrg ms_dri2_frame_event_abort); 79135c4bbdfSmrg if (!seq) 79235c4bbdfSmrg goto out_free; 79335c4bbdfSmrg 7941b5d61b8Smrg if (!ms_queue_vblank(crtc, MS_QUEUE_ABSOLUTE, request_msc, &queued_msc, seq)) { 79535c4bbdfSmrg static int limit = 5; 79635c4bbdfSmrg if (limit) { 79735c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 79835c4bbdfSmrg "%s:%d get vblank counter failed: %s\n", 79935c4bbdfSmrg __FUNCTION__, __LINE__, 80035c4bbdfSmrg strerror(errno)); 80135c4bbdfSmrg limit--; 80235c4bbdfSmrg } 80335c4bbdfSmrg goto out_free; 80435c4bbdfSmrg } 80535c4bbdfSmrg 8061b5d61b8Smrg wait_info->frame = queued_msc; 8071b5d61b8Smrg 80835c4bbdfSmrg DRI2BlockClient(client, draw); 80935c4bbdfSmrg 81035c4bbdfSmrg return TRUE; 81135c4bbdfSmrg 81235c4bbdfSmrg out_free: 81335c4bbdfSmrg ms_dri2_del_frame_event(wait_info); 81435c4bbdfSmrg out_complete: 81535c4bbdfSmrg DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); 81635c4bbdfSmrg return TRUE; 81735c4bbdfSmrg} 81835c4bbdfSmrg 81935c4bbdfSmrg/** 82035c4bbdfSmrg * ScheduleSwap is responsible for requesting a DRM vblank event for 82135c4bbdfSmrg * the appropriate frame, or executing the swap immediately if it 82235c4bbdfSmrg * doesn't need to wait. 82335c4bbdfSmrg * 82435c4bbdfSmrg * When the swap is complete, the driver should call into the server so it 82535c4bbdfSmrg * can send any swap complete events that have been requested. 82635c4bbdfSmrg */ 82735c4bbdfSmrgstatic int 82835c4bbdfSmrgms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, 82935c4bbdfSmrg DRI2BufferPtr front, DRI2BufferPtr back, 83035c4bbdfSmrg CARD64 *target_msc, CARD64 divisor, 83135c4bbdfSmrg CARD64 remainder, DRI2SwapEventPtr func, void *data) 83235c4bbdfSmrg{ 83335c4bbdfSmrg ScreenPtr screen = draw->pScreen; 83435c4bbdfSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 8351b5d61b8Smrg int ret, flip = 0; 83635c4bbdfSmrg xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw); 83735c4bbdfSmrg ms_dri2_frame_event_ptr frame_info = NULL; 83835c4bbdfSmrg uint64_t current_msc, current_ust; 83935c4bbdfSmrg uint64_t request_msc; 84035c4bbdfSmrg uint32_t seq; 8411b5d61b8Smrg ms_queue_flag ms_flag = MS_QUEUE_ABSOLUTE; 8421b5d61b8Smrg uint64_t queued_msc; 84335c4bbdfSmrg 84435c4bbdfSmrg /* Drawable not displayed... just complete the swap */ 84535c4bbdfSmrg if (!crtc) 84635c4bbdfSmrg goto blit_fallback; 84735c4bbdfSmrg 84835c4bbdfSmrg frame_info = calloc(1, sizeof(*frame_info)); 84935c4bbdfSmrg if (!frame_info) 85035c4bbdfSmrg goto blit_fallback; 85135c4bbdfSmrg 85235c4bbdfSmrg frame_info->screen = screen; 85335c4bbdfSmrg frame_info->drawable = draw; 85435c4bbdfSmrg frame_info->client = client; 85535c4bbdfSmrg frame_info->event_complete = func; 85635c4bbdfSmrg frame_info->event_data = data; 85735c4bbdfSmrg frame_info->front = front; 85835c4bbdfSmrg frame_info->back = back; 85935c4bbdfSmrg frame_info->crtc = crtc; 86035c4bbdfSmrg frame_info->type = MS_DRI2_QUEUE_SWAP; 86135c4bbdfSmrg 86235c4bbdfSmrg if (!ms_dri2_add_frame_event(frame_info)) { 86335c4bbdfSmrg free(frame_info); 86435c4bbdfSmrg frame_info = NULL; 86535c4bbdfSmrg goto blit_fallback; 86635c4bbdfSmrg } 86735c4bbdfSmrg 86835c4bbdfSmrg ms_dri2_reference_buffer(front); 86935c4bbdfSmrg ms_dri2_reference_buffer(back); 87035c4bbdfSmrg 87135c4bbdfSmrg ret = ms_get_crtc_ust_msc(crtc, ¤t_ust, ¤t_msc); 8721b5d61b8Smrg if (ret != Success) 8731b5d61b8Smrg goto blit_fallback; 8741b5d61b8Smrg 8751b5d61b8Smrg /* Flips need to be submitted one frame before */ 8761b5d61b8Smrg if (can_flip(scrn, draw, front, back)) { 8771b5d61b8Smrg frame_info->type = MS_DRI2_QUEUE_FLIP; 8781b5d61b8Smrg flip = 1; 8791b5d61b8Smrg } 8801b5d61b8Smrg 8811b5d61b8Smrg /* Correct target_msc by 'flip' if frame_info->type == MS_DRI2_QUEUE_FLIP. 8821b5d61b8Smrg * Do it early, so handling of different timing constraints 8831b5d61b8Smrg * for divisor, remainder and msc vs. target_msc works. 8841b5d61b8Smrg */ 8851b5d61b8Smrg if (*target_msc > 0) 8861b5d61b8Smrg *target_msc -= flip; 8871b5d61b8Smrg 8881b5d61b8Smrg /* If non-pageflipping, but blitting/exchanging, we need to use 8891b5d61b8Smrg * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later 8901b5d61b8Smrg * on. 8911b5d61b8Smrg */ 8921b5d61b8Smrg if (flip == 0) 8931b5d61b8Smrg ms_flag |= MS_QUEUE_NEXT_ON_MISS; 89435c4bbdfSmrg 89535c4bbdfSmrg /* 89635c4bbdfSmrg * If divisor is zero, or current_msc is smaller than target_msc 89735c4bbdfSmrg * we just need to make sure target_msc passes before initiating 89835c4bbdfSmrg * the swap. 89935c4bbdfSmrg */ 90035c4bbdfSmrg if (divisor == 0 || current_msc < *target_msc) { 90135c4bbdfSmrg 90235c4bbdfSmrg /* If target_msc already reached or passed, set it to 90335c4bbdfSmrg * current_msc to ensure we return a reasonable value back 90435c4bbdfSmrg * to the caller. This makes swap_interval logic more robust. 90535c4bbdfSmrg */ 90635c4bbdfSmrg if (current_msc >= *target_msc) 90735c4bbdfSmrg *target_msc = current_msc; 90835c4bbdfSmrg 90935c4bbdfSmrg seq = ms_drm_queue_alloc(crtc, frame_info, 91035c4bbdfSmrg ms_dri2_frame_event_handler, 91135c4bbdfSmrg ms_dri2_frame_event_abort); 91235c4bbdfSmrg if (!seq) 91335c4bbdfSmrg goto blit_fallback; 91435c4bbdfSmrg 9151b5d61b8Smrg if (!ms_queue_vblank(crtc, ms_flag, *target_msc, &queued_msc, seq)) { 91635c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 91735c4bbdfSmrg "divisor 0 get vblank counter failed: %s\n", 91835c4bbdfSmrg strerror(errno)); 91935c4bbdfSmrg goto blit_fallback; 92035c4bbdfSmrg } 92135c4bbdfSmrg 9221b5d61b8Smrg *target_msc = queued_msc + flip; 92335c4bbdfSmrg frame_info->frame = *target_msc; 92435c4bbdfSmrg 92535c4bbdfSmrg return TRUE; 92635c4bbdfSmrg } 92735c4bbdfSmrg 92835c4bbdfSmrg /* 92935c4bbdfSmrg * If we get here, target_msc has already passed or we don't have one, 93035c4bbdfSmrg * and we need to queue an event that will satisfy the divisor/remainder 93135c4bbdfSmrg * equation. 93235c4bbdfSmrg */ 93335c4bbdfSmrg 93435c4bbdfSmrg request_msc = current_msc - (current_msc % divisor) + 93535c4bbdfSmrg remainder; 93635c4bbdfSmrg 93735c4bbdfSmrg /* 93835c4bbdfSmrg * If the calculated deadline vbl.request.sequence is smaller than 93935c4bbdfSmrg * or equal to current_msc, it means we've passed the last point 94035c4bbdfSmrg * when effective onset frame seq could satisfy 94135c4bbdfSmrg * seq % divisor == remainder, so we need to wait for the next time 94235c4bbdfSmrg * this will happen. 94335c4bbdfSmrg 94435c4bbdfSmrg * This comparison takes the DRM_VBLANK_NEXTONMISS delay into account. 94535c4bbdfSmrg */ 94635c4bbdfSmrg if (request_msc <= current_msc) 94735c4bbdfSmrg request_msc += divisor; 94835c4bbdfSmrg 94935c4bbdfSmrg seq = ms_drm_queue_alloc(crtc, frame_info, 95035c4bbdfSmrg ms_dri2_frame_event_handler, 95135c4bbdfSmrg ms_dri2_frame_event_abort); 95235c4bbdfSmrg if (!seq) 95335c4bbdfSmrg goto blit_fallback; 95435c4bbdfSmrg 9551b5d61b8Smrg /* Account for 1 frame extra pageflip delay if flip > 0 */ 9561b5d61b8Smrg if (!ms_queue_vblank(crtc, ms_flag, request_msc - flip, &queued_msc, seq)) { 95735c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 95835c4bbdfSmrg "final get vblank counter failed: %s\n", 95935c4bbdfSmrg strerror(errno)); 96035c4bbdfSmrg goto blit_fallback; 96135c4bbdfSmrg } 96235c4bbdfSmrg 9631b5d61b8Smrg /* Adjust returned value for 1 fame pageflip offset of flip > 0 */ 9641b5d61b8Smrg *target_msc = queued_msc + flip; 96535c4bbdfSmrg frame_info->frame = *target_msc; 96635c4bbdfSmrg 96735c4bbdfSmrg return TRUE; 96835c4bbdfSmrg 96935c4bbdfSmrg blit_fallback: 97035c4bbdfSmrg ms_dri2_blit_swap(draw, front, back); 97135c4bbdfSmrg DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data); 97235c4bbdfSmrg if (frame_info) 97335c4bbdfSmrg ms_dri2_del_frame_event(frame_info); 97435c4bbdfSmrg *target_msc = 0; /* offscreen, so zero out target vblank count */ 97535c4bbdfSmrg return TRUE; 97635c4bbdfSmrg} 97735c4bbdfSmrg 97835c4bbdfSmrgstatic int 97935c4bbdfSmrgms_dri2_frame_event_client_gone(void *data, XID id) 98035c4bbdfSmrg{ 98135c4bbdfSmrg struct ms_dri2_resource *resource = data; 98235c4bbdfSmrg 98335c4bbdfSmrg while (!xorg_list_is_empty(&resource->list)) { 98435c4bbdfSmrg ms_dri2_frame_event_ptr info = 98535c4bbdfSmrg xorg_list_first_entry(&resource->list, 98635c4bbdfSmrg ms_dri2_frame_event_rec, 98735c4bbdfSmrg client_resource); 98835c4bbdfSmrg 98935c4bbdfSmrg xorg_list_del(&info->client_resource); 99035c4bbdfSmrg info->client = NULL; 99135c4bbdfSmrg } 99235c4bbdfSmrg free(resource); 99335c4bbdfSmrg 99435c4bbdfSmrg return Success; 99535c4bbdfSmrg} 99635c4bbdfSmrg 99735c4bbdfSmrgstatic int 99835c4bbdfSmrgms_dri2_frame_event_drawable_gone(void *data, XID id) 99935c4bbdfSmrg{ 100035c4bbdfSmrg struct ms_dri2_resource *resource = data; 100135c4bbdfSmrg 100235c4bbdfSmrg while (!xorg_list_is_empty(&resource->list)) { 100335c4bbdfSmrg ms_dri2_frame_event_ptr info = 100435c4bbdfSmrg xorg_list_first_entry(&resource->list, 100535c4bbdfSmrg ms_dri2_frame_event_rec, 100635c4bbdfSmrg drawable_resource); 100735c4bbdfSmrg 100835c4bbdfSmrg xorg_list_del(&info->drawable_resource); 100935c4bbdfSmrg info->drawable = NULL; 101035c4bbdfSmrg } 101135c4bbdfSmrg free(resource); 101235c4bbdfSmrg 101335c4bbdfSmrg return Success; 101435c4bbdfSmrg} 101535c4bbdfSmrg 101635c4bbdfSmrgstatic Bool 101735c4bbdfSmrgms_dri2_register_frame_event_resource_types(void) 101835c4bbdfSmrg{ 101935c4bbdfSmrg frame_event_client_type = 102035c4bbdfSmrg CreateNewResourceType(ms_dri2_frame_event_client_gone, 102135c4bbdfSmrg "Frame Event Client"); 102235c4bbdfSmrg if (!frame_event_client_type) 102335c4bbdfSmrg return FALSE; 102435c4bbdfSmrg 102535c4bbdfSmrg frame_event_drawable_type = 102635c4bbdfSmrg CreateNewResourceType(ms_dri2_frame_event_drawable_gone, 102735c4bbdfSmrg "Frame Event Drawable"); 102835c4bbdfSmrg if (!frame_event_drawable_type) 102935c4bbdfSmrg return FALSE; 103035c4bbdfSmrg 103135c4bbdfSmrg return TRUE; 103235c4bbdfSmrg} 103335c4bbdfSmrg 103435c4bbdfSmrgBool 103535c4bbdfSmrgms_dri2_screen_init(ScreenPtr screen) 103635c4bbdfSmrg{ 103735c4bbdfSmrg ScrnInfoPtr scrn = xf86ScreenToScrn(screen); 103835c4bbdfSmrg modesettingPtr ms = modesettingPTR(scrn); 103935c4bbdfSmrg DRI2InfoRec info; 10405a7dfde8Smrg const char *driver_names[2] = { NULL, NULL }; 104135c4bbdfSmrg 1042ed6184dfSmrg if (!ms->glamor.supports_pixmap_import_export(screen)) { 104335c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 104435c4bbdfSmrg "DRI2: glamor lacks support for pixmap import/export\n"); 104535c4bbdfSmrg } 104635c4bbdfSmrg 104735c4bbdfSmrg if (!xf86LoaderCheckSymbol("DRI2Version")) 104835c4bbdfSmrg return FALSE; 104935c4bbdfSmrg 105035c4bbdfSmrg if (!dixRegisterPrivateKey(&ms_dri2_client_key, 105135c4bbdfSmrg PRIVATE_CLIENT, sizeof(XID))) 105235c4bbdfSmrg return FALSE; 105335c4bbdfSmrg 105435c4bbdfSmrg if (serverGeneration != ms_dri2_server_generation) { 105535c4bbdfSmrg ms_dri2_server_generation = serverGeneration; 105635c4bbdfSmrg if (!ms_dri2_register_frame_event_resource_types()) { 105735c4bbdfSmrg xf86DrvMsg(scrn->scrnIndex, X_WARNING, 105835c4bbdfSmrg "Cannot register DRI2 frame event resources\n"); 105935c4bbdfSmrg return FALSE; 106035c4bbdfSmrg } 106135c4bbdfSmrg } 106235c4bbdfSmrg 106335c4bbdfSmrg memset(&info, '\0', sizeof(info)); 106435c4bbdfSmrg info.fd = ms->fd; 106535c4bbdfSmrg info.driverName = NULL; /* Compat field, unused. */ 106635c4bbdfSmrg info.deviceName = drmGetDeviceNameFromFd(ms->fd); 106735c4bbdfSmrg 10681b5d61b8Smrg info.version = 9; 106935c4bbdfSmrg info.CreateBuffer = ms_dri2_create_buffer; 107035c4bbdfSmrg info.DestroyBuffer = ms_dri2_destroy_buffer; 107135c4bbdfSmrg info.CopyRegion = ms_dri2_copy_region; 107235c4bbdfSmrg info.ScheduleSwap = ms_dri2_schedule_swap; 107335c4bbdfSmrg info.GetMSC = ms_dri2_get_msc; 107435c4bbdfSmrg info.ScheduleWaitMSC = ms_dri2_schedule_wait_msc; 10751b5d61b8Smrg info.CreateBuffer2 = ms_dri2_create_buffer2; 10761b5d61b8Smrg info.DestroyBuffer2 = ms_dri2_destroy_buffer2; 10771b5d61b8Smrg info.CopyRegion2 = ms_dri2_copy_region2; 107835c4bbdfSmrg 1079ed6184dfSmrg /* Ask Glamor to obtain the DRI driver name via EGL_MESA_query_driver, */ 1080ed6184dfSmrg if (ms->glamor.egl_get_driver_name) 1081ed6184dfSmrg driver_names[0] = ms->glamor.egl_get_driver_name(screen); 10825a7dfde8Smrg 10835a7dfde8Smrg if (driver_names[0]) { 10845a7dfde8Smrg /* There is no VDPAU driver for Intel, fallback to the generic 10855a7dfde8Smrg * OpenGL/VAAPI va_gl backend to emulate VDPAU. Otherwise, 10865a7dfde8Smrg * guess that the DRI and VDPAU drivers have the same name. 10875a7dfde8Smrg */ 10885a7dfde8Smrg if (strcmp(driver_names[0], "i965") == 0 || 1089ed6184dfSmrg strcmp(driver_names[0], "iris") == 0 || 1090ed6184dfSmrg strcmp(driver_names[0], "crocus") == 0) { 10915a7dfde8Smrg driver_names[1] = "va_gl"; 10925a7dfde8Smrg } else { 10935a7dfde8Smrg driver_names[1] = driver_names[0]; 10945a7dfde8Smrg } 10955a7dfde8Smrg 10965a7dfde8Smrg info.numDrivers = 2; 10975a7dfde8Smrg info.driverNames = driver_names; 10985a7dfde8Smrg } else { 10995a7dfde8Smrg /* EGL_MESA_query_driver was unavailable; let dri2.c select the 11005a7dfde8Smrg * driver and fill in these fields for us. 11015a7dfde8Smrg */ 11025a7dfde8Smrg info.numDrivers = 0; 11035a7dfde8Smrg info.driverNames = NULL; 11045a7dfde8Smrg } 110535c4bbdfSmrg 110635c4bbdfSmrg return DRI2ScreenInit(screen, &info); 110735c4bbdfSmrg} 110835c4bbdfSmrg 110935c4bbdfSmrgvoid 111035c4bbdfSmrgms_dri2_close_screen(ScreenPtr screen) 111135c4bbdfSmrg{ 111235c4bbdfSmrg DRI2CloseScreen(screen); 111335c4bbdfSmrg} 111435c4bbdfSmrg 11151b5d61b8Smrg#endif /* GLAMOR_HAS_GBM */ 1116