blob: 57e2a6ddc9da221c80ca8c55da0c4b0e0b35bbda [file] [log] [blame]
/* -*- 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 "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);
}
}