| /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ |
| |
| /* |
| * Copyright © 2011 Texas Instruments, Inc |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * Authors: |
| * Rob Clark <rob@ti.com> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "omap_driver.h" |
| #include "omap_exa.h" |
| |
| #include "xf86drmMode.h" |
| #include "dri2.h" |
| |
| /* any point to support earlier? */ |
| #if DRI2INFOREC_VERSION < 4 |
| # error "Requires newer DRI2" |
| #endif |
| |
| |
| typedef struct { |
| DRI2BufferRec base; |
| |
| /** |
| * Pixmap that is backing the buffer |
| * |
| * NOTE: don't track the pixmap ptr for the front buffer if it is |
| * a window.. this could get reallocated from beneath us, so we should |
| * always use draw2pix to be sure to have the correct one |
| */ |
| PixmapPtr pPixmap; |
| |
| /** |
| * The DRI2 buffers are reference counted to avoid crashyness when the |
| * client detaches a dri2 drawable while we are still waiting for a |
| * page_flip event. |
| */ |
| int refcnt; |
| |
| } OMAPDRI2BufferRec, *OMAPDRI2BufferPtr; |
| |
| #define OMAPBUF(p) ((OMAPDRI2BufferPtr)(p)) |
| #define DRIBUF(p) ((DRI2BufferPtr)(&(p)->base)) |
| |
| |
| static inline DrawablePtr |
| dri2draw(DrawablePtr pDraw, DRI2BufferPtr buf) |
| { |
| if (buf->attachment == DRI2BufferFrontLeft) { |
| return pDraw; |
| } else { |
| return &(OMAPBUF(buf)->pPixmap->drawable); |
| } |
| } |
| |
| static inline Bool |
| canexchange(DrawablePtr pDraw, DRI2BufferPtr a, DRI2BufferPtr b) |
| { |
| DrawablePtr da = dri2draw(pDraw, a); |
| DrawablePtr db = dri2draw(pDraw, b); |
| |
| return DRI2CanFlip(pDraw) && |
| (da->width == db->width) && |
| (da->height == db->height) && |
| (da->depth == db->depth); |
| } |
| |
| static Bool |
| canflip(DrawablePtr pDraw) |
| { |
| return (pDraw->type == DRAWABLE_WINDOW) && |
| DRI2CanFlip(pDraw); |
| } |
| |
| static inline Bool |
| exchangebufs(DrawablePtr pDraw, DRI2BufferPtr a, DRI2BufferPtr b) |
| { |
| OMAPPixmapExchange(draw2pix(dri2draw(pDraw, a)), |
| draw2pix(dri2draw(pDraw, b))); |
| exchange(a->name, b->name); |
| return TRUE; |
| } |
| |
| static PixmapPtr |
| createpix(DrawablePtr pDraw) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| int flags = canflip(pDraw) ? OMAP_CREATE_PIXMAP_SCANOUT : 0; |
| return pScreen->CreatePixmap(pScreen, |
| pDraw->width, pDraw->height, pDraw->depth, flags); |
| } |
| |
| /** |
| * Create Buffer. |
| * |
| * Note that 'format' is used from the client side to specify the DRI buffer |
| * format, which could differ from the drawable format. For example, the |
| * drawable could be 32b RGB, but the DRI buffer some YUV format (video) or |
| * perhaps lower bit depth RGB (GL). The color conversion is handled when |
| * blitting to front buffer, and page-flipping (overlay or flipchain) can |
| * only be used if the display supports. |
| */ |
| static DRI2BufferPtr |
| OMAPDRI2CreateBuffer(DrawablePtr pDraw, unsigned int attachment, |
| unsigned int format) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| OMAPDRI2BufferPtr buf = calloc(1, sizeof(*buf)); |
| PixmapPtr pPixmap; |
| struct omap_bo *bo; |
| int ret; |
| |
| DEBUG_MSG("pDraw=%p, attachment=%d, format=%08x", |
| pDraw, attachment, format); |
| |
| if (!buf) { |
| return NULL; |
| } |
| |
| if (attachment == DRI2BufferFrontLeft) { |
| pPixmap = draw2pix(pDraw); |
| |
| /* to do flipping, if we don't have DMM, then we need a scanout |
| * capable (physically contiguous) buffer.. this bit of gymnastics |
| * ensures that. |
| * |
| * TODO we may want to re-allocate and switch back to non-scanout |
| * buffer when client disconnects from drawable.. |
| */ |
| |
| /* TODO: We don't have enough memory to allocate three physically contiguous buffers at the same time! Because all our |
| * buffers are scanout-able we'll not bother allocating *another* scanout buffer and just use the one we already have |
| * and save that extra buffer size */ |
| #if 0 |
| if (canflip(pDraw) && !has_dmm(pOMAP) && |
| (OMAPPixmapBo(pPixmap) != pOMAP->scanout)) { |
| |
| /* need to re-allocate pixmap to get a scanout capable buffer */ |
| PixmapPtr pNewPix = createpix(pDraw); |
| |
| // TODO copy contents.. |
| |
| OMAPPixmapExchange(pPixmap, pNewPix); |
| |
| pScreen->DestroyPixmap(pNewPix); |
| } |
| #endif |
| |
| pPixmap->refcnt++; |
| } else { |
| pPixmap = createpix(pDraw); |
| } |
| |
| bo = OMAPPixmapBo(pPixmap); |
| if (!bo) |
| { |
| ERROR_MSG("Attempting to DRI2 wrap a pixmap with no DRM buffer object backing"); |
| /* TODO: Returning NULL here ends up in a segfault all the way in pixman which has no backtrace. We get |
| * a more friendly segfault if we just let it be dereferenced in a few lines */ |
| } |
| |
| DRIBUF(buf)->attachment = attachment; |
| DRIBUF(buf)->pitch = exaGetPixmapPitch(pPixmap); |
| DRIBUF(buf)->cpp = pPixmap->drawable.bitsPerPixel / 8; |
| DRIBUF(buf)->format = format; |
| DRIBUF(buf)->flags = 0; |
| buf->refcnt = 1; |
| buf->pPixmap = pPixmap; |
| |
| ret = omap_bo_get_name(bo, &DRIBUF(buf)->name); |
| if (ret) { |
| ERROR_MSG("could not get buffer name: %d", ret); |
| /* TODO cleanup */ |
| return NULL; |
| } |
| |
| /* Q: how to know across OMAP generations what formats that the display |
| * can support directly? |
| * A: attempt to create a drm_framebuffer, and if that fails then the |
| * hw must not support.. then fall back to blitting |
| */ |
| if (canflip(pDraw) && attachment != DRI2BufferFrontLeft) { |
| uint32_t new_fb_id; |
| int ret = drmModeAddFB(pOMAP->drmFD, pDraw->width, pDraw->height, |
| pDraw->depth, pDraw->bitsPerPixel, DRIBUF(buf)->pitch, |
| omap_bo_handle(bo), &new_fb_id); |
| if (ret) { |
| /* to-bad, so-sad, we can't flip */ |
| WARNING_MSG("could not create fb: %d", ret); |
| } else { |
| omap_bo_set_fb(bo, new_fb_id); |
| } |
| } |
| |
| return DRIBUF(buf); |
| } |
| |
| /** |
| * Destroy Buffer |
| * |
| * TODO: depending on how flipping ends up working, we may need a refcnt or |
| * something like this to defer destroying a buffer that is currently being |
| * scanned out.. |
| */ |
| static void |
| OMAPDRI2DestroyBuffer(DrawablePtr pDraw, DRI2BufferPtr buffer) |
| { |
| OMAPDRI2BufferPtr buf = OMAPBUF(buffer); |
| /* Note: pDraw may already be deleted, so use the pPixmap here |
| * instead (since it is at least refcntd) |
| */ |
| ScreenPtr pScreen = buf->pPixmap->drawable.pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| |
| if (--buf->refcnt > 0) |
| return; |
| |
| DEBUG_MSG("pDraw=%p, buffer=%p", pDraw, buffer); |
| |
| pScreen->DestroyPixmap(buf->pPixmap); |
| |
| free(buf); |
| } |
| |
| static void |
| OMAPDRI2ReferenceBuffer(DRI2BufferPtr buffer) |
| { |
| OMAPDRI2BufferPtr buf = OMAPBUF(buffer); |
| buf->refcnt++; |
| } |
| |
| /** |
| * |
| */ |
| static void |
| OMAPDRI2CopyRegion(DrawablePtr pDraw, RegionPtr pRegion, |
| DRI2BufferPtr pDstBuffer, DRI2BufferPtr pSrcBuffer) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| DrawablePtr pSrcDraw = dri2draw(pDraw, pSrcBuffer); |
| DrawablePtr pDstDraw = dri2draw(pDraw, pDstBuffer); |
| RegionPtr pCopyClip; |
| GCPtr pGC; |
| |
| DEBUG_MSG("pDraw=%p, pDstBuffer=%p (%p), pSrcBuffer=%p (%p)", |
| pDraw, pDstBuffer, pSrcDraw, pSrcBuffer, pDstDraw); |
| |
| pGC = GetScratchGC(pDstDraw->depth, pScreen); |
| if (!pGC) { |
| return; |
| } |
| |
| pCopyClip = REGION_CREATE(pScreen, NULL, 0); |
| RegionCopy(pCopyClip, pRegion); |
| (*pGC->funcs->ChangeClip) (pGC, CT_REGION, pCopyClip, 0); |
| ValidateGC(pDstDraw, pGC); |
| |
| /* If the dst is the framebuffer, and we had a way to |
| * schedule a deferred blit synchronized w/ vsync, that |
| * would be a nice thing to do utilize here to avoid |
| * tearing.. when we have sync object support for GEM |
| * buffers, I think we could do something more clever |
| * here. |
| */ |
| |
| pGC->ops->CopyArea(pSrcDraw, pDstDraw, pGC, |
| 0, 0, pDraw->width, pDraw->height, 0, 0); |
| |
| FreeScratchGC(pGC); |
| } |
| |
| /** |
| * Get current frame count and frame count timestamp, based on drawable's |
| * crtc. |
| */ |
| static int |
| OMAPDRI2GetMSC(DrawablePtr pDraw, CARD64 *ust, CARD64 *msc) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| drmVBlank vbl = { .request = { |
| .type = DRM_VBLANK_RELATIVE, |
| .sequence = 0, |
| } }; |
| int ret; |
| |
| ret = drmWaitVBlank(pOMAP->drmFD, &vbl); |
| if (ret) { |
| static int limit = 5; |
| if (limit) { |
| ERROR_MSG("get vblank counter failed: %s", strerror(errno)); |
| limit--; |
| } |
| return FALSE; |
| } |
| |
| if (ust) { |
| *ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec; |
| } |
| if (msc) { |
| *msc = vbl.reply.sequence; |
| } |
| |
| return TRUE; |
| } |
| |
| struct _OMAPDRISwapCmd { |
| int type; |
| ClientPtr client; |
| ScreenPtr pScreen; |
| /* Note: store drawable ID, rather than drawable. It's possible that |
| * the drawable can be destroyed while we wait for page flip event: |
| */ |
| XID draw_id; |
| DRI2BufferPtr pDstBuffer; |
| DRI2BufferPtr pSrcBuffer; |
| DRI2SwapEventPtr func; |
| void *data; |
| }; |
| |
| static const char *swap_names[] = { |
| [DRI2_EXCHANGE_COMPLETE] = "exchange", |
| [DRI2_BLIT_COMPLETE] = "blit", |
| [DRI2_FLIP_COMPLETE] = "flip," |
| }; |
| |
| void |
| OMAPDRI2SwapComplete(OMAPDRISwapCmd *cmd) |
| { |
| ScreenPtr pScreen = cmd->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| DrawablePtr pDraw = NULL; |
| int status; |
| OMAPPixmapPrivPtr dst_priv; |
| |
| DEBUG_MSG("%s complete: %d -> %d", swap_names[cmd->type], |
| cmd->pSrcBuffer->attachment, cmd->pDstBuffer->attachment); |
| |
| status = dixLookupDrawable(&pDraw, cmd->draw_id, serverClient, |
| M_ANY, DixWriteAccess); |
| |
| if (status == Success) { |
| if (cmd->type != DRI2_BLIT_COMPLETE) |
| exchangebufs(pDraw, cmd->pSrcBuffer, cmd->pDstBuffer); |
| |
| DRI2SwapComplete(cmd->client, pDraw, 0, 0, 0, cmd->type, |
| cmd->func, cmd->data); |
| } |
| |
| dst_priv = exaGetPixmapDriverPrivate(draw2pix(dri2draw(pDraw, cmd->pDstBuffer))); |
| |
| if (cmd->type != DRI2_BLIT_COMPLETE) |
| set_scanout_bo(pScrn, dst_priv->bo); |
| |
| /* drop extra refcnt we obtained prior to swap: |
| */ |
| OMAPDRI2DestroyBuffer(pDraw, cmd->pSrcBuffer); |
| OMAPDRI2DestroyBuffer(pDraw, cmd->pDstBuffer); |
| pOMAP->pending_flips--; |
| |
| free(cmd); |
| } |
| |
| /** |
| * ScheduleSwap is responsible for requesting a DRM vblank event for the |
| * appropriate frame. |
| * |
| * In the case of a blit (e.g. for a windowed swap) or buffer exchange, |
| * the vblank requested can simply be the last queued swap frame + the swap |
| * interval for the drawable. |
| * |
| * In the case of a page flip, we request an event for the last queued swap |
| * frame + swap interval - 1, since we'll need to queue the flip for the frame |
| * immediately following the received event. |
| */ |
| static int |
| OMAPDRI2ScheduleSwap(ClientPtr client, DrawablePtr pDraw, |
| DRI2BufferPtr pDstBuffer, DRI2BufferPtr pSrcBuffer, |
| CARD64 *target_msc, CARD64 divisor, CARD64 remainder, |
| DRI2SwapEventPtr func, void *data) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| OMAPDRI2BufferPtr src = OMAPBUF(pSrcBuffer); |
| OMAPDRI2BufferPtr dst = OMAPBUF(pDstBuffer); |
| OMAPDRISwapCmd *cmd = calloc(1, sizeof(*cmd)); |
| int src_fb_id, dst_fb_id; |
| OMAPPixmapPrivPtr src_priv, dst_priv; |
| |
| cmd->client = client; |
| cmd->pScreen = pScreen; |
| cmd->draw_id = pDraw->id; |
| cmd->pSrcBuffer = pSrcBuffer; |
| cmd->pDstBuffer = pDstBuffer; |
| cmd->func = func; |
| cmd->data = data; |
| |
| DEBUG_MSG("%d -> %d", pSrcBuffer->attachment, pDstBuffer->attachment); |
| |
| /* obtain extra ref on buffers to avoid them going away while we await |
| * the page flip event: |
| */ |
| OMAPDRI2ReferenceBuffer(pSrcBuffer); |
| OMAPDRI2ReferenceBuffer(pDstBuffer); |
| pOMAP->pending_flips++; |
| |
| src_priv = exaGetPixmapDriverPrivate(src->pPixmap); |
| dst_priv = exaGetPixmapDriverPrivate(dst->pPixmap); |
| |
| src_fb_id = omap_bo_get_fb(src_priv->bo); |
| dst_fb_id = omap_bo_get_fb(dst_priv->bo); |
| |
| /* Currently only one FD logic is applied for clone mode. |
| * Need to find a way for swapping in v4l based HDMI*/ |
| |
| if(!(pOMAP->hdmi)) { |
| if (src_fb_id && dst_fb_id && canflip(pDraw)) { |
| DEBUG_MSG("can flip: %d -> %d", src_fb_id, dst_fb_id); |
| cmd->type = DRI2_FLIP_COMPLETE; |
| drmmode_page_flip(pDraw, src_fb_id, cmd); |
| } else if (canexchange(pDraw, pSrcBuffer, pDstBuffer) && dst_fb_id ) { |
| /* we can get away w/ pointer swap.. yah! */ |
| cmd->type = DRI2_EXCHANGE_COMPLETE; |
| OMAPDRI2SwapComplete(cmd); |
| } |
| } else { |
| /* fallback to blit: */ |
| BoxRec box = { |
| .x1 = 0, |
| .y1 = 0, |
| .x2 = pDraw->width, |
| .y2 = pDraw->height, |
| }; |
| RegionRec region; |
| RegionInit(®ion, &box, 0); |
| OMAPDRI2CopyRegion(pDraw, ®ion, pDstBuffer, pSrcBuffer); |
| cmd->type = DRI2_BLIT_COMPLETE; |
| OMAPDRI2SwapComplete(cmd); |
| } |
| |
| return TRUE; |
| } |
| |
| /** |
| * Request a DRM event when the requested conditions will be satisfied. |
| * |
| * We need to handle the event and ask the server to wake up the client when |
| * we receive it. |
| */ |
| static int |
| OMAPDRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc, |
| CARD64 divisor, CARD64 remainder) |
| { |
| ScreenPtr pScreen = pDraw->pScreen; |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| // OMAPPtr pOMAP = OMAPPTR(pScrn); |
| |
| #if 0 |
| #endif |
| ERROR_MSG("not implemented"); |
| return FALSE; |
| } |
| |
| /** |
| * The DRI2 ScreenInit() function.. register our handler fxns w/ DRI2 core |
| */ |
| Bool |
| OMAPDRI2ScreenInit(ScreenPtr pScreen) |
| { |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| DRI2InfoRec info = { |
| .version = 5, |
| .fd = pOMAP->drmFD, |
| .driverName = "armsoc", |
| .deviceName = pOMAP->deviceName, |
| .CreateBuffer = OMAPDRI2CreateBuffer, |
| .DestroyBuffer = OMAPDRI2DestroyBuffer, |
| .CopyRegion = OMAPDRI2CopyRegion, |
| .ScheduleSwap = OMAPDRI2ScheduleSwap, |
| .ScheduleWaitMSC = OMAPDRI2ScheduleWaitMSC, |
| .GetMSC = OMAPDRI2GetMSC, |
| .AuthMagic = drmAuthMagic, |
| }; |
| int minor = 1, major = 0; |
| |
| if (xf86LoaderCheckSymbol("DRI2Version")) { |
| DRI2Version(&major, &minor); |
| } |
| |
| if (minor < 1) { |
| WARNING_MSG("DRI2 requires DRI2 module version 1.1.0 or later"); |
| return FALSE; |
| } |
| |
| return DRI2ScreenInit(pScreen, &info); |
| } |
| |
| /** |
| * The DRI2 CloseScreen() function.. unregister ourself w/ DRI2 core. |
| */ |
| void |
| OMAPDRI2CloseScreen(ScreenPtr pScreen) |
| { |
| ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; |
| OMAPPtr pOMAP = OMAPPTR(pScrn); |
| while (pOMAP->pending_flips > 0) { |
| DEBUG_MSG("waiting.."); |
| drmmode_wait_for_event(pScrn); |
| } |
| DRI2CloseScreen(pScreen); |
| } |