diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2010-11-17 17:39:39 -0500 |
---|---|---|
committer | Alex Deucher <alexdeucher@gmail.com> | 2010-12-01 20:30:30 -0500 |
commit | 0de680730294bd623f6b3e189faa7b88a09d3a2a (patch) | |
tree | 0b310deb8c167d10fa24314b3c13328b095f8eaa | |
parent | fccdca8db34010f566bd068c74cdef0f4a8cb7f5 (diff) |
radeon/kms: add pageflip support
requires radeon drm 2.8.0 or higher
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Jerome Glisse <jglisse@redhat.com>
-rw-r--r-- | src/drmmode_display.c | 90 | ||||
-rw-r--r-- | src/drmmode_display.h | 4 | ||||
-rw-r--r-- | src/radeon.h | 2 | ||||
-rw-r--r-- | src/radeon_dri2.c | 215 | ||||
-rw-r--r-- | src/radeon_dri2.h | 2 | ||||
-rw-r--r-- | src/radeon_kms.c | 8 |
6 files changed, 299 insertions, 22 deletions
diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 33012315..1310da7c 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -29,6 +29,7 @@ #include "config.h" #endif +#include <errno.h> #ifdef XF86DRM_MODE #include <sys/ioctl.h> #include "micmap.h" @@ -1280,6 +1281,25 @@ drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, } static void +drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + drmmode_ptr drmmode = event_data; + + drmmode->flip_count--; + if (drmmode->flip_count > 0) + return; + + drmModeRmFB(drmmode->fd, drmmode->old_fb_id); + + if (drmmode->event_data == NULL) + return; + + radeon_dri2_flip_event_handler(frame, tv_sec, tv_usec, drmmode->event_data); +} + + +static void drm_wakeup_handler(pointer data, int err, pointer p) { drmmode_ptr drmmode = data; @@ -1322,8 +1342,9 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp) drmmode->flip_count = 0; drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION; drmmode->event_context.vblank_handler = drmmode_vblank_handler; - drmmode->event_context.page_flip_handler = NULL; + drmmode->event_context.page_flip_handler = drmmode_flip_handler; if (!pRADEONEnt->fd_wakeup_registered && info->dri->pKernelDRMVersion->version_minor >= 4) { + drmmode->flip_count = 0; AddGeneralSocket(drmmode->fd); RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, drm_wakeup_handler, drmmode); @@ -1563,4 +1584,71 @@ void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) #endif } +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data) +{ + RADEONInfoPtr info = RADEONPTR(scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + unsigned int pitch = scrn->displayWidth * info->CurrentLayout.pixel_bytes; + int i, old_fb_id; + uint32_t tiling_flags = 0; + int height; + + if (info->allowColorTiling) { + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling_flags |= RADEON_TILING_MICRO; + else + tiling_flags |= RADEON_TILING_MACRO; + } + + pitch = RADEON_ALIGN(pitch, drmmode_get_pitch_align(scrn, info->CurrentLayout.pixel_bytes, tiling_flags)); + height = RADEON_ALIGN(scrn->virtualY, drmmode_get_height_align(scrn, tiling_flags)); + + /* + * Create a new handle for the back buffer + */ + old_fb_id = drmmode->fb_id; + if (drmModeAddFB(drmmode->fd, scrn->virtualX, height, + scrn->depth, scrn->bitsPerPixel, pitch, + new_front->handle, &drmmode->fb_id)) + goto error_out; + + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + for (i = 0; i < config->num_crtc; i++) { + if (!config->crtc[i]->enabled) + continue; + + drmmode->event_data = data; + drmmode->flip_count++; + drmmode_crtc = config->crtc[i]->driver_private; + if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, drmmode)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + goto error_undo; + } + } + + drmmode->old_fb_id = old_fb_id; + return TRUE; + +error_undo: + drmModeRmFB(drmmode->fd, drmmode->fb_id); + drmmode->fb_id = old_fb_id; + +error_out: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", + strerror(errno)); + return FALSE; +} + #endif diff --git a/src/drmmode_display.h b/src/drmmode_display.h index e6bfd50a..c3d3561f 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -39,6 +39,7 @@ typedef struct { int fd; unsigned fb_id; + unsigned old_fb_id; drmModeResPtr mode_res; drmModeFBPtr mode_fb; int cpp; @@ -50,6 +51,7 @@ typedef struct { #endif drmEventContext event_context; int flip_count; + void *event_data; } drmmode_rec, *drmmode_ptr; typedef struct { @@ -99,6 +101,8 @@ extern int drmmode_get_height_align(ScrnInfoPtr scrn, uint32_t tiling); extern int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe, uint32_t tiling); extern int drmmode_get_base_align(ScrnInfoPtr scrn, int bpe, uint32_t tiling); +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data); + #endif #endif diff --git a/src/radeon.h b/src/radeon.h index 84d3563b..0f7d012e 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -1074,6 +1074,8 @@ typedef struct { struct radeon_bo *bicubic_bo; void *bicubic_memory; int bicubic_offset; + /* kms pageflipping */ + Bool allowPageFlip; } RADEONInfoRec, *RADEONInfoPtr; #define RADEONWaitForFifo(pScrn, entries) \ diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index f2ea0bb4..69f8a480 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -82,6 +82,10 @@ radeon_dri2_create_buffers(DrawablePtr drawable, struct radeon_exa_pixmap_priv *driver_priv; int i, r, need_enlarge = 0; int flags = 0; + unsigned front_width; + + pixmap = screen->GetScreenPixmap(screen); + front_width = pixmap->drawable.width; buffers = calloc(count, sizeof *buffers); if (buffers == NULL) { @@ -162,12 +166,18 @@ radeon_dri2_create_buffers(DrawablePtr drawable, drawable->depth, flags); - } else + } else { + unsigned aligned_width = drawable->width; + + if (aligned_width == front_width) + aligned_width = pScrn->virtualX; + pixmap = (*pScreen->CreatePixmap)(pScreen, - drawable->width, + aligned_width, drawable->height, drawable->depth, flags); + } } if (attachments[i] == DRI2BufferDepth) { @@ -206,6 +216,10 @@ radeon_dri2_create_buffer(DrawablePtr drawable, struct radeon_exa_pixmap_priv *driver_priv; int r, need_enlarge = 0; int flags; + unsigned front_width; + + pixmap = pScreen->GetScreenPixmap(pScreen); + front_width = pixmap->drawable.width; buffers = calloc(1, sizeof *buffers); if (buffers == NULL) { @@ -287,12 +301,18 @@ radeon_dri2_create_buffer(DrawablePtr drawable, (format != 0)?format:drawable->depth, flags); - } else + } else { + unsigned aligned_width = drawable->width; + + if (aligned_width == front_width) + aligned_width = pScrn->virtualX; + pixmap = (*pScreen->CreatePixmap)(pScreen, - drawable->width, + aligned_width, drawable->height, (format != 0)?format:drawable->depth, flags); + } } if (attachment == DRI2BufferDepth) { @@ -549,12 +569,98 @@ radeon_dri2_unref_buffer(BufferPtr buffer) } } +static Bool +radeon_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client, + DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, + void *data) +{ + struct dri2_buffer_priv *back_priv; + struct radeon_exa_pixmap_priv *exa_priv; + DRI2FrameEventPtr flip_info; + + flip_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!flip_info) + return FALSE; + + flip_info->drawable_id = draw->id; + flip_info->client = client; + flip_info->type = DRI2_SWAP; + flip_info->event_complete = func; + flip_info->event_data = data; + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, + "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); + + /* Page flip the full screen buffer */ + back_priv = back->driverPrivate; + exa_priv = exaGetPixmapDriverPrivate(back_priv->pixmap); + return radeon_do_pageflip(scrn, exa_priv->bo, flip_info); +} + +static Bool +can_exchange(DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + PixmapPtr front_pixmap = front_priv->pixmap; + PixmapPtr back_pixmap = back_priv->pixmap; + + if (front_pixmap->drawable.width != back_pixmap->drawable.width) + return FALSE; + + if (front_pixmap->drawable.height != back_pixmap->drawable.height) + return FALSE; + + if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel) + return FALSE; + + if (front_pixmap->devKind != back_pixmap->devKind) + return FALSE; + + return TRUE; +} + +static void +radeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + struct radeon_exa_pixmap_priv *front_radeon, *back_radeon; + ScreenPtr screen; + RADEONInfoPtr info; + struct radeon_bo *bo; + int tmp; + + /* Swap BO names so DRI works */ + tmp = front->name; + front->name = back->name; + back->name = tmp; + + /* Swap pixmap bos */ + front_radeon = exaGetPixmapDriverPrivate(front_priv->pixmap); + back_radeon = exaGetPixmapDriverPrivate(back_priv->pixmap); + bo = back_radeon->bo; + back_radeon->bo = front_radeon->bo; + front_radeon->bo = bo; + + /* Do we need to update the Screen? */ + screen = draw->pScreen; + info = RADEONPTR(xf86Screens[screen->myNum]); + if (front_radeon->bo == info->front_bo) { + radeon_bo_unref(info->front_bo); + info->front_bo = back_radeon->bo; + radeon_bo_ref(info->front_bo); + front_radeon = exaGetPixmapDriverPrivate(screen->GetScreenPixmap(screen)); + front_radeon->bo = bo; + } +} + void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data) { DRI2FrameEventPtr event = event_data; + RADEONInfoPtr info; DrawablePtr drawable; - ClientPtr client; ScreenPtr screen; ScrnInfoPtr scrn; int status; @@ -563,7 +669,7 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, RegionRec region; if (!event->valid) - goto cleanup; + goto cleanup; status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, M_ANY, DixWriteAccess); @@ -572,25 +678,45 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, screen = drawable->pScreen; scrn = xf86Screens[screen->myNum]; - client = event->client; + info = RADEONPTR(scrn); switch (event->type) { case DRI2_FLIP: + if (info->allowPageFlip && + DRI2CanFlip(drawable) && + can_exchange(event->front, event->back) && + radeon_dri2_schedule_flip(scrn, + event->client, + drawable, + event->front, + event->back, + event->event_complete, + event->event_data)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + break; + } + /* else fall through to exchange/blit */ case DRI2_SWAP: - box.x1 = 0; - box.y1 = 0; - box.x2 = drawable->width; - box.y2 = drawable->height; - REGION_INIT(pScreen, ®ion, &box, 0); - radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); - swap_type = DRI2_BLIT_COMPLETE; - - DRI2SwapComplete(client, drawable, frame, tv_sec, tv_usec, + if (DRI2CanExchange(drawable) && + can_exchange(event->front, event->back)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + swap_type = DRI2_EXCHANGE_COMPLETE; + } else { + box.x1 = 0; + box.y1 = 0; + box.x2 = drawable->width; + box.y2 = drawable->height; + REGION_INIT(pScreen, ®ion, &box, 0); + radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); + swap_type = DRI2_BLIT_COMPLETE; + } + + DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec, swap_type, event->event_complete, event->event_data); break; case DRI2_WAITMSC: - DRI2WaitMSCComplete(client, drawable, frame, tv_sec, tv_usec); + DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, tv_usec); break; default: /* Unknown type */ @@ -638,7 +764,7 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) RADEONInfoPtr info = RADEONPTR(scrn); drmVBlank vbl; int ret; - int crtc= radeon_dri2_drawable_crtc(draw); + int crtc = radeon_dri2_drawable_crtc(draw); /* Drawable not displayed, make up a value */ if (crtc == -1) { @@ -796,6 +922,47 @@ out_complete: return TRUE; } +void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr flip = event_data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + int status; + PixmapPtr pixmap; + + status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) { + free(flip); + return; + } + + screen = drawable->pScreen; + scrn = xf86Screens[screen->myNum]; + + pixmap = screen->GetScreenPixmap(screen); + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, + "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", + __func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4); + + /* We assume our flips arrive in order, so we don't check the frame */ + switch (flip->type) { + case DRI2_SWAP: + DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, + DRI2_FLIP_COMPLETE, flip->event_complete, + flip->event_data); + break; + default: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__); + /* Unknown type */ + break; + } + + free(flip); +} + /* * ScheduleSwap is responsible for requesting a DRM vblank event for the * appropriate frame. @@ -883,6 +1050,15 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, } current_msc = vbl.reply.sequence; + + /* Flips need to be submitted one frame before */ + if (info->allowPageFlip && + DRI2CanFlip(draw) && + can_exchange(front, back)) { + swap_type = DRI2_FLIP; + flip = 1; + } + swap_info->type = swap_type; /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. @@ -899,9 +1075,6 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, */ if (divisor == 0 || current_msc < *target_msc) { vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; - if (crtc > 0) - vbl.request.type |= DRM_VBLANK_SECONDARY; - /* If non-pageflipping, but blitting/exchanging, we need to use * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later * on. diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h index 688530f2..79952862 100644 --- a/src/radeon_dri2.h +++ b/src/radeon_dri2.h @@ -44,5 +44,7 @@ xf86CrtcPtr radeon_covering_crtc(ScrnInfoPtr pScrn, BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret); void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data); +void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data); #endif diff --git a/src/radeon_kms.c b/src/radeon_kms.c index 122ac298..38467c07 100644 --- a/src/radeon_kms.c +++ b/src/radeon_kms.c @@ -70,6 +70,7 @@ const OptionInfoRec RADEONOptions_KMS[] = { { OPTION_EXA_VSYNC, "EXAVSync", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_EXA_PIXMAPS, "EXAPixmaps", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE }, + { OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE }, { -1, NULL, OPTV_NONE, {0}, FALSE } }; @@ -620,6 +621,13 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "KMS Color Tiling: %sabled\n", info->allowColorTiling ? "en" : "dis"); + if (info->dri->pKernelDRMVersion->version_minor >= 8) { + info->allowPageFlip = xf86ReturnOptValBool(info->Options, + OPTION_PAGE_FLIP, TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "KMS Pageflipping: %sabled\n", info->allowPageFlip ? "en" : "dis"); + } + if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == FALSE) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n"); goto fail; |