/* -*- 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "armsoc_exa.h" #include "armsoc_driver.h" /* keep this here, instead of static-inline so submodule doesn't * need to know layout of ARMSOCRec. */ _X_EXPORT struct ARMSOCEXARec * ARMSOCEXAPTR(ScrnInfoPtr pScrn) { struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn); return pARMSOC->pARMSOCEXA; } /* Common ARMSOC EXA functions, mostly related to pixmap/buffer allocation. * Individual driver submodules can use these directly, or wrap them with * there own functions if anything additional is required. Submodules * can use ARMSOCPrixmapPrivPtr#priv for their own private data. */ /* used by DRI2 code to play buffer switcharoo */ void ARMSOCPixmapExchange(PixmapPtr a, PixmapPtr b) { struct ARMSOCPixmapPrivRec *apriv = exaGetPixmapDriverPrivate(a); struct ARMSOCPixmapPrivRec *bpriv = exaGetPixmapDriverPrivate(b); exchange(apriv->priv, bpriv->priv); exchange(apriv->bo, bpriv->bo); /* Ensure neither pixmap has a dmabuf fd attached to the bo if the * ext_access_cnt refcount is 0, as it will never be cleared. */ if (armsoc_bo_has_dmabuf(apriv->bo) && !apriv->ext_access_cnt) { armsoc_bo_clear_dmabuf(apriv->bo); /* Should only have to clear one dmabuf fd, otherwise the * refcount is wrong */ assert(!armsoc_bo_has_dmabuf(bpriv->bo)); } else if (armsoc_bo_has_dmabuf(bpriv->bo) && !bpriv->ext_access_cnt) { armsoc_bo_clear_dmabuf(bpriv->bo); assert(!armsoc_bo_has_dmabuf(apriv->bo)); } } _X_EXPORT void * ARMSOCCreatePixmap2(ScreenPtr pScreen, int width, int height, int depth, int usage_hint, int bitsPerPixel, int *new_fb_pitch) { struct ARMSOCPixmapPrivRec *priv = calloc(sizeof(struct ARMSOCPixmapPrivRec), 1); ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn); enum armsoc_buf_type buf_type = ARMSOC_BO_NON_SCANOUT; if (!priv) return NULL; if (usage_hint & ARMSOC_CREATE_PIXMAP_SCANOUT) buf_type = ARMSOC_BO_SCANOUT; if (width > 0 && height > 0 && depth > 0 && bitsPerPixel > 0) { priv->bo = armsoc_bo_new_with_dim(pARMSOC->dev, width, height, depth, bitsPerPixel, buf_type); if ((!priv->bo) && ARMSOC_BO_SCANOUT == buf_type) { /* Tried to create a scanout but failed. Attempt to * fall back to non-scanout instead. */ WARNING_MSG( "Scanout buffer allocation failed, falling back to non-scanout"); buf_type = ARMSOC_BO_NON_SCANOUT; priv->bo = armsoc_bo_new_with_dim(pARMSOC->dev, width, height, depth, bitsPerPixel, buf_type); } if (!priv->bo) { ERROR_MSG("failed to allocate %dx%d bo, buf_type = %d", width, height, buf_type); free(priv); return NULL; } *new_fb_pitch = armsoc_bo_pitch(priv->bo); } /* The usage_hint field of the Pixmap passed to ModifyPixmapHeader is * not set to the usage_hint parameter passed to CreatePixmap. * It does appear to be set here so we stash it in the private * structure. However as we do not fully understand the uses of this * parameter, beware of any unexpected values! */ priv->usage_hint = usage_hint; return priv; } _X_EXPORT void ARMSOCDestroyPixmap(ScreenPtr pScreen, void *driverPriv) { struct ARMSOCPixmapPrivRec *priv = driverPriv; assert(!priv->ext_access_cnt); /* If ModifyPixmapHeader failed, it's possible we don't have a bo * backing this pixmap. */ if (priv->bo) { assert(!armsoc_bo_has_dmabuf(priv->bo)); armsoc_bo_unreference(priv->bo); } free(priv); } _X_EXPORT Bool ARMSOCModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth, int bitsPerPixel, int devKind, pointer pPixData) { struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); ScrnInfoPtr pScrn = pix2scrn(pPixmap); struct ARMSOCRec *pARMSOC = ARMSOCPTR(pScrn); enum armsoc_buf_type buf_type = ARMSOC_BO_NON_SCANOUT; /* Only modify specified fields, keeping all others intact. */ if (pPixData) pPixmap->devPrivate.ptr = pPixData; if (devKind > 0) pPixmap->devKind = devKind; /* * We can't accelerate this pixmap, and don't ever want to * see it again.. */ if (pPixData && pPixData != armsoc_bo_map(pARMSOC->scanout)) { /* scratch-pixmap (see GetScratchPixmapHeader()) gets recycled, * so could have a previous bo! */ armsoc_bo_unreference(priv->bo); priv->bo = NULL; /* Returning FALSE calls miModifyPixmapHeader */ return FALSE; } if (pPixData == armsoc_bo_map(pARMSOC->scanout)) priv->bo = pARMSOC->scanout; if (priv->usage_hint & ARMSOC_CREATE_PIXMAP_SCANOUT) buf_type = ARMSOC_BO_SCANOUT; if (depth > 0) pPixmap->drawable.depth = depth; if (bitsPerPixel > 0) pPixmap->drawable.bitsPerPixel = bitsPerPixel; if (width > 0) pPixmap->drawable.width = width; if (height > 0) pPixmap->drawable.height = height; /* * X will sometimes create an empty pixmap (width/height == 0) and then * use ModifyPixmapHeader to point it at PixData. We'll hit this path * during the CreatePixmap call. Just return true and skip the allocate * in this case. */ if (!pPixmap->drawable.width || !pPixmap->drawable.height) return TRUE; if (!priv->bo || armsoc_bo_width(priv->bo) != pPixmap->drawable.width || armsoc_bo_height(priv->bo) != pPixmap->drawable.height || armsoc_bo_bpp(priv->bo) != pPixmap->drawable.bitsPerPixel) { /* re-allocate buffer! */ armsoc_bo_unreference(priv->bo); priv->bo = armsoc_bo_new_with_dim(pARMSOC->dev, pPixmap->drawable.width, pPixmap->drawable.height, pPixmap->drawable.depth, pPixmap->drawable.bitsPerPixel, buf_type); if ((!priv->bo) && ARMSOC_BO_SCANOUT == buf_type) { /* Tried to create a scanout but failed. Attempt to * fall back to non-scanout instead. */ WARNING_MSG( "Scanout buffer allocation failed, falling back to non-scanout"); buf_type = ARMSOC_BO_NON_SCANOUT; priv->bo = armsoc_bo_new_with_dim(pARMSOC->dev, pPixmap->drawable.width, pPixmap->drawable.height, pPixmap->drawable.depth, pPixmap->drawable.bitsPerPixel, buf_type); } if (!priv->bo) { ERROR_MSG("failed to allocate %dx%d bo, buf_type = %d", pPixmap->drawable.width, pPixmap->drawable.height, buf_type); return FALSE; } pPixmap->devKind = armsoc_bo_pitch(priv->bo); } return TRUE; } /** * WaitMarker is a required EXA callback but synchronization is * performed during ARMSOCPrepareAccess so this function does not * have anything to do at present */ _X_EXPORT void ARMSOCWaitMarker(ScreenPtr pScreen, int marker) { /* no-op */ } static inline enum armsoc_gem_op idx2op(int index) { switch (index) { case EXA_PREPARE_SRC: case EXA_PREPARE_MASK: case EXA_PREPARE_AUX_SRC: case EXA_PREPARE_AUX_MASK: return ARMSOC_GEM_READ; case EXA_PREPARE_AUX_DEST: case EXA_PREPARE_DEST: default: return ARMSOC_GEM_READ_WRITE; } } /** * PrepareAccess() is called before CPU access to an offscreen pixmap. * * @param pPix the pixmap being accessed * @param index the index of the pixmap being accessed. * * PrepareAccess() will be called before CPU access to an offscreen pixmap. * This can be used to set up hardware surfaces for byteswapping or * untiling, or to adjust the pixmap's devPrivate.ptr for the purpose of * making CPU access use a different aperture. * * The index is one of #EXA_PREPARE_DEST, #EXA_PREPARE_SRC, * #EXA_PREPARE_MASK, #EXA_PREPARE_AUX_DEST, #EXA_PREPARE_AUX_SRC, or * #EXA_PREPARE_AUX_MASK. Since only up to #EXA_NUM_PREPARE_INDICES pixmaps * will have PrepareAccess() called on them per operation, drivers can have * a small, statically-allocated space to maintain state for PrepareAccess() * and FinishAccess() in. Note that PrepareAccess() is only called once per * pixmap and operation, regardless of whether the pixmap is used as a * destination and/or source, and the index may not reflect the usage. * * PrepareAccess() may fail. An example might be the case of hardware that * can set up 1 or 2 surfaces for CPU access, but not 3. If PrepareAccess() * fails, EXA will migrate the pixmap to system memory. * DownloadFromScreen() must be implemented and must not fail if a driver * wishes to fail in PrepareAccess(). PrepareAccess() must not fail when * pPix is the visible screen, because the visible screen can not be * migrated. * * @return TRUE if PrepareAccess() successfully prepared the pixmap for CPU * drawing. * @return FALSE if PrepareAccess() is unsuccessful and EXA should use * DownloadFromScreen() to migate the pixmap out. */ _X_EXPORT Bool ARMSOCPrepareAccess(PixmapPtr pPixmap, int index) { struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); pPixmap->devPrivate.ptr = armsoc_bo_map(priv->bo); if (!pPixmap->devPrivate.ptr) { xf86DrvMsg(-1, X_ERROR, "%s: Failed to map buffer\n", __func__); return FALSE; } /* Attach dmabuf fd to bo to synchronise access if * pixmap wrapped by DRI2 */ if (priv->ext_access_cnt && !armsoc_bo_has_dmabuf(priv->bo)) { if (armsoc_bo_set_dmabuf(priv->bo)) { xf86DrvMsg(-1, X_ERROR, "%s: Unable to get dma_buf fd for bo, to enable synchronised CPU access.\n", __func__); return FALSE; } } if (armsoc_bo_cpu_prep(priv->bo, idx2op(index))) { xf86DrvMsg(-1, X_ERROR, "%s: armsoc_bo_cpu_prep failed - unable to synchronise access.\n", __func__); return FALSE; } return TRUE; } /** * FinishAccess() is called after CPU access to an offscreen pixmap. * * @param pPix the pixmap being accessed * @param index the index of the pixmap being accessed. * * FinishAccess() will be called after finishing CPU access of an offscreen * pixmap set up by PrepareAccess(). Note that the FinishAccess() will not be * called if PrepareAccess() failed and the pixmap was migrated out. */ _X_EXPORT void ARMSOCFinishAccess(PixmapPtr pPixmap, int index) { struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); pPixmap->devPrivate.ptr = NULL; /* NOTE: can we use EXA migration module to track which parts of the * buffer was accessed by sw, and pass that info down to kernel to * do a more precise cache flush.. */ armsoc_bo_cpu_fini(priv->bo, idx2op(index)); } /** * PixmapIsOffscreen() is an optional driver replacement to * exaPixmapHasGpuCopy(). Set to NULL if you want the standard behaviour * of exaPixmapHasGpuCopy(). * * @param pPix the pixmap * @return TRUE if the given drawable is in framebuffer memory. * * exaPixmapHasGpuCopy() is used to determine if a pixmap is in * offscreen memory, meaning that acceleration could probably be done * to it, and that it will need to be wrapped by PrepareAccess()/ * FinishAccess() when accessing it from the CPU. */ _X_EXPORT Bool ARMSOCPixmapIsOffscreen(PixmapPtr pPixmap) { /* offscreen means in 'gpu accessible memory', not that it's off * the visible screen. We currently have no special constraints, * since compatible ARM CPUS have a flat memory model (no separate * GPU memory). If an individual EXA implementation has additional * constraints, like buffer size or mapping in GPU MMU, it should * wrap this function. */ struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); return priv && priv->bo; } void ARMSOCRegisterExternalAccess(PixmapPtr pPixmap) { struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); priv->ext_access_cnt++; } void ARMSOCDeregisterExternalAccess(PixmapPtr pPixmap) { struct ARMSOCPixmapPrivRec *priv = exaGetPixmapDriverPrivate(pPixmap); assert(priv->ext_access_cnt > 0); priv->ext_access_cnt--; if (priv->ext_access_cnt == 0) { /* No DRI2 buffers wrapping the pixmap, so no * need for synchronisation with dma_buf */ if (armsoc_bo_has_dmabuf(priv->bo)) armsoc_bo_clear_dmabuf(priv->bo); } }