xenocara/driver/xf86-video-openchrome/libxvmc/viaXvMC.c
matthieu 855916fc83 update to openchrome.net svn commit 750.
This adds among others support for the VX800 chipset.
This has been in snapshots for weeks.
2009-06-25 21:27:18 +00:00

1948 lines
54 KiB
C

/*****************************************************************************
* VIA Unichrome XvMC extension client lib.
*
* Copyright (c) 2004-2005 Thomas Hellström. All rights reserved.
*
* 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 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
* AUTHOR(S) OR COPYRIGHT HOLDER(S) 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.
*/
/*
*Author: Thomas Hellström, 2004.
*Bugfixes by among others Pascal Brisset and Terry Barnaby.
*DRI protocol support by Thomas Hellström, 2005.
*/
#undef WAITPAUSE
#include "viaXvMCPriv.h"
#include "viaLowLevel.h"
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <time.h>
#include <fourcc.h>
#include <X11/extensions/Xv.h>
#include <xf86drm.h>
#include <pthread.h>
#include "vldXvMC.h"
#include "xf86dri.h"
#include "driDrawable.h"
#define SAREAPTR(ctx) ((ViaXvMCSAreaPriv *) \
(((CARD8 *)(ctx)->sAreaAddress) + \
(ctx)->sAreaPrivOffset))
typedef struct
{
int major;
int minor;
int patchlevel;
} ViaDRMVersion;
static int error_base;
static int event_base;
static unsigned numContexts = 0;
static int globalFD;
static drmAddress sAreaAddress;
static drmAddress fbAddress;
static drmAddress mmioAddress;
static const ViaDRMVersion drmExpected = { 2, 0, 0 };
static const ViaDRMVersion drmCompat = { 2, 0, 0 };
#define FOURCC_XVMC (('C' << 24) + ('M' << 16) + ('V' << 8) + 'X')
#define ppthread_mutex_lock(arg) \
{ \
pthread_mutex_lock(arg); \
} \
#define ppthread_mutex_unlock(arg) \
{ \
pthread_mutex_unlock(arg); \
} \
static unsigned
yOffs(ViaXvMCSurface * srf)
{
return srf->offsets[0];
}
static unsigned
vOffs(ViaXvMCSurface * srf)
{
return srf->offsets[0] + srf->yStride * srf->height;
}
static unsigned
uOffs(ViaXvMCSurface * srf)
{
return srf->offsets[0] + (srf->yStride * srf->height) +
(srf->yStride >> 1) * (srf->height >> 1);
}
static void
defaultQMatrices(ViaXvMCContext * ctx)
{
int i;
static const char intra[64] = {
8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83
};
for (i = 0; i < 64; ++i) {
ctx->intra_quantiser_matrix[i] = intra[i];
ctx->non_intra_quantiser_matrix[i] = 16;
}
ctx->intraLoaded = 0;
ctx->nonIntraLoaded = 0;
}
static void
releaseDecoder(ViaXvMCContext * ctx, int clearCtx)
{
volatile ViaXvMCSAreaPriv *sAPriv;
sAPriv = SAREAPTR(ctx);
UNICHROME_UNLOCK(ctx->fd, UNICHROME_LOCK_DECODER1, sAPriv,
ctx->drmcontext);
}
static int
grabDecoder(ViaXvMCContext * ctx, int *hadLastLock)
{
volatile ViaXvMCSAreaPriv *sAPriv = SAREAPTR(ctx);
int retFtx, lc;
/*
* Try to grab the decoder. If it is not available we will sleep until
* it becomes available or for a maximum of 20 ms.
* Then try to grab it again, unless a timeout occured. If the decoder is
* available, the lock should be reasonably fast.
*/
if (ctx->haveDecoder) {
flushXvMCLowLevel(ctx->xl); /* Ignore errors here. */
/*fprintf(stderr,"ViaXvMC: ERROR: Trying to re-lock decoder.\n"); */
*hadLastLock = 1;
return 0;
}
UNICHROME_LOCK(ctx->fd, UNICHROME_LOCK_DECODER1, sAPriv, ctx->drmcontext,
lc, retFtx);
*hadLastLock = (ctx->drmcontext == lc);
return retFtx;
}
static void
setupAttribDesc(Display * display, XvPortID port,
const ViaXvMCAttrHolder * attrib, XvAttribute attribDesc[])
{
XvAttribute *XvAttribs, *curAD;
int num;
unsigned i, j;
XLockDisplay(display);
XvAttribs = XvQueryPortAttributes(display, port, &num);
for (i = 0; i < attrib->numAttr; ++i) {
curAD = attribDesc + i;
curAD->flags = 0;
curAD->min_value = 0;
curAD->max_value = 0;
curAD->name = NULL;
for (j = 0; j < num; ++j) {
if (attrib->attributes[i].attribute ==
XInternAtom(display, XvAttribs[j].name, TRUE)) {
*curAD = XvAttribs[j];
curAD->name = strdup(XvAttribs[j].name);
break;
}
}
}
if (XvAttribs)
XFree(XvAttribs);
XUnlockDisplay(display);
}
static void
releaseAttribDesc(int numAttr, XvAttribute attribDesc[])
{
int i;
for (i = 0; i < numAttr; ++i) {
if (attribDesc[i].name)
free(attribDesc[i].name);
}
}
static Status
releaseContextResources(Display * display, XvMCContext * context,
int freePrivate, Status errType)
{
ViaXvMCContext *pViaXvMC = (ViaXvMCContext *) context->privData;
switch (pViaXvMC->resources) {
case context_drawHash:
driDestroyHashContents(pViaXvMC->drawHash);
drmHashDestroy(pViaXvMC->drawHash);
case context_lowLevel:
closeXvMCLowLevel(pViaXvMC->xl);
case context_mutex:
pthread_mutex_destroy(&pViaXvMC->ctxMutex);
case context_drmContext:
XLockDisplay(display);
uniDRIDestroyContext(display, pViaXvMC->screen, pViaXvMC->id);
XUnlockDisplay(display);
case context_sAreaMap:
numContexts--;
if (numContexts == 0)
drmUnmap(pViaXvMC->sAreaAddress, pViaXvMC->sAreaSize);
case context_fbMap:
if (numContexts == 0)
drmUnmap(pViaXvMC->fbAddress, pViaXvMC->fbSize);
case context_mmioMap:
if (numContexts == 0)
drmUnmap(pViaXvMC->mmioAddress, pViaXvMC->mmioSize);
case context_fd:
if (numContexts == 0) {
if (pViaXvMC->fd >= 0)
drmClose(pViaXvMC->fd);
}
pViaXvMC->fd = -1;
case context_driConnection:
if (numContexts == 0) {
XLockDisplay(display);
uniDRICloseConnection(display, pViaXvMC->screen);
XUnlockDisplay(display);
}
case context_context:
XLockDisplay(display);
_xvmc_destroy_context(display, context);
XUnlockDisplay(display);
if (!freePrivate)
break;
default:
free(pViaXvMC);
context->privData = NULL;
}
return errType;
}
_X_EXPORT Status
XvMCCreateContext(Display * display, XvPortID port,
int surface_type_id, int width, int height, int flags,
XvMCContext * context)
{
ViaXvMCContext *pViaXvMC;
int priv_count;
uint *priv_data;
uint magic;
unsigned i;
Status ret;
int major, minor;
ViaXvMCCreateContextRec *tmpComm;
drmVersionPtr drmVer;
char *curBusID;
int isCapable;
/*
* Verify Obvious things first
*/
if (context == NULL) {
return XvMCBadContext;
}
if (!(flags & XVMC_DIRECT)) {
fprintf(stderr, "Indirect Rendering not supported! Using Direct.\n");
}
/*
*FIXME: Check $DISPLAY for legal values here
*/
context->surface_type_id = surface_type_id;
context->width = (unsigned short)((width + 15) & ~15);
context->height = (unsigned short)((height + 15) & ~15);
context->flags = flags;
context->port = port;
/*
* Width, Height, and flags are checked against surface_type_id
* and port for validity inside the X server, no need to check
* here.
*/
/* Allocate private Context data */
context->privData = (void *)malloc(sizeof(ViaXvMCContext));
if (!context->privData) {
fprintf(stderr, "Unable to allocate resources for XvMC context.\n");
return BadAlloc;
}
pViaXvMC = (ViaXvMCContext *) context->privData;
pViaXvMC->resources = context_none;
/* Verify the XvMC extension exists */
XLockDisplay(display);
if (!XvMCQueryExtension(display, &event_base, &error_base)) {
fprintf(stderr, "XvMC Extension is not available!\n");
free(pViaXvMC);
XUnlockDisplay(display);
return BadAlloc;
}
/* Verify XvMC version */
ret = XvMCQueryVersion(display, &major, &minor);
if (ret) {
fprintf(stderr, "XvMCQuery Version Failed, unable to determine "
"protocol version!\n");
}
XUnlockDisplay(display);
/* FIXME: Check Major and Minor here */
XLockDisplay(display);
if ((ret = _xvmc_create_context(display, context, &priv_count,
&priv_data))) {
XUnlockDisplay(display);
fprintf(stderr, "Unable to create XvMC Context!\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
XUnlockDisplay(display);
/*
* Check size and version of returned data.
*/
tmpComm = (ViaXvMCCreateContextRec *) priv_data;
if (priv_count != (sizeof(ViaXvMCCreateContextRec) >> 2)) {
fprintf(stderr, "_xvmc_create_context() returned incorrect "
"data size!\n");
fprintf(stderr, "\tExpected %d, got %d\n",
((int)(sizeof(ViaXvMCCreateContextRec) >> 2)), priv_count);
XFree(priv_data);
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->resources = context_context;
if ((tmpComm->major != VIAXVMC_MAJOR) ||
(tmpComm->minor != VIAXVMC_MINOR)) {
fprintf(stderr, "Version mismatch between the X via driver\n"
"and the XvMC library. Cannot continue!\n");
XFree(priv_data);
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->ctxNo = tmpComm->ctxNo;
pViaXvMC->fbOffset = tmpComm->fbOffset;
pViaXvMC->fbSize = tmpComm->fbSize;
pViaXvMC->mmioOffset = tmpComm->mmioOffset;
pViaXvMC->mmioSize = tmpComm->mmioSize;
pViaXvMC->sAreaSize = tmpComm->sAreaSize;
pViaXvMC->sAreaPrivOffset = tmpComm->sAreaPrivOffset;
pViaXvMC->decoderOn = 0;
pViaXvMC->xvMCPort = tmpComm->xvmc_port;
pViaXvMC->useAGP = tmpComm->useAGP;
pViaXvMC->attrib = tmpComm->initAttrs;
pViaXvMC->screen = tmpComm->screen;
pViaXvMC->depth = tmpComm->depth;
pViaXvMC->stride = tmpComm->stride;
pViaXvMC->chipId = tmpComm->chipId;
/*
* Must free the private data we were passed from X
*/
XFree(priv_data);
priv_data = NULL;
/*
* Check for direct rendering capable, establish DRI and DRM connections,
* map framebuffer, DRI shared area and read-only register areas.
* Initial checking for drm has already been done by the server.
* Only do this for the first context we create.
*/
if (numContexts == 0) {
XLockDisplay(display);
ret =
uniDRIQueryDirectRenderingCapable(display, pViaXvMC->screen,
&isCapable);
if (!ret || !isCapable) {
XUnlockDisplay(display);
fprintf(stderr,
"Direct Rendering is not available on this system!\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
if (!uniDRIOpenConnection(display, pViaXvMC->screen,
&pViaXvMC->sAreaOffset, &curBusID)) {
XUnlockDisplay(display);
fprintf(stderr, "Could not open DRI connection to X server!\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
XUnlockDisplay(display);
strncpy(pViaXvMC->busIdString, curBusID, 20);
pViaXvMC->busIdString[20] = '\0';
XFree(curBusID);
pViaXvMC->resources = context_driConnection;
if ((pViaXvMC->fd = drmOpen("via", pViaXvMC->busIdString)) < 0) {
fprintf(stderr, "DRM Device for via could not be opened.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
globalFD = pViaXvMC->fd;
pViaXvMC->resources = context_fd;
if (NULL == (drmVer = drmGetVersion(pViaXvMC->fd))) {
fprintf(stderr, "viaXvMC: Could not get drm version.");
return releaseContextResources(display, context, 1, BadAlloc);
}
if ((drmVer->version_major < drmExpected.major) ||
(drmVer->version_major > drmCompat.major) ||
((drmVer->version_major == drmExpected.major) &&
(drmVer->version_minor < drmExpected.minor))) {
fprintf(stderr,
"viaXvMC: Kernel drm is not compatible with XvMC.\n");
fprintf(stderr,
"viaXvMC: Kernel drm version: %d.%d.%d "
"and I can work with versions %d.%d.x - %d.x.x\n"
"Please update either this XvMC driver or your kernel DRM.\n",
drmVer->version_major, drmVer->version_minor,
drmVer->version_patchlevel, drmExpected.major,
drmExpected.minor, drmCompat.major);
drmFreeVersion(drmVer);
return releaseContextResources(display, context, 1, BadAlloc);
}
drmFreeVersion(drmVer);
drmGetMagic(pViaXvMC->fd, &magic);
XLockDisplay(display);
if (!uniDRIAuthConnection(display, pViaXvMC->screen, magic)) {
XUnlockDisplay(display);
fprintf(stderr,
"viaXvMC: X server did not allow DRI. Check permissions.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
XUnlockDisplay(display);
/*
* Map the register memory
*/
if (drmMap(pViaXvMC->fd, pViaXvMC->mmioOffset,
pViaXvMC->mmioSize, &mmioAddress) < 0) {
fprintf(stderr,
"Unable to map the display chip mmio registers.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->mmioAddress = mmioAddress;
pViaXvMC->resources = context_mmioMap;
/*
* Map Framebuffer memory
*/
if (drmMap(pViaXvMC->fd, pViaXvMC->fbOffset,
pViaXvMC->fbSize, &fbAddress) < 0) {
fprintf(stderr, "Unable to map XvMC Framebuffer.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->fbAddress = fbAddress;
pViaXvMC->resources = context_fbMap;
/*
* Map DRI Sarea.
*/
if (drmMap(pViaXvMC->fd, pViaXvMC->sAreaOffset,
pViaXvMC->sAreaSize, &sAreaAddress) < 0) {
fprintf(stderr, "Unable to map DRI SAREA.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
} else {
pViaXvMC->fd = globalFD;
pViaXvMC->mmioAddress = mmioAddress;
pViaXvMC->fbAddress = fbAddress;
}
pViaXvMC->sAreaAddress = sAreaAddress;
pViaXvMC->resources = context_sAreaMap;
numContexts++;
/*
* Find a matching visual. Important only for direct drawing to the visible
* frame-buffer.
*/
XLockDisplay(display);
ret = XMatchVisualInfo(display, pViaXvMC->screen,
(pViaXvMC->depth == 32) ? 24 : pViaXvMC->depth, TrueColor,
&pViaXvMC->visualInfo);
XUnlockDisplay(display);
if (!ret) {
fprintf(stderr,
"viaXvMC: Could not find a matching TrueColor visual.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
if (!uniDRICreateContext(display, pViaXvMC->screen,
pViaXvMC->visualInfo.visual, &pViaXvMC->id,
&pViaXvMC->drmcontext)) {
fprintf(stderr, "viaXvMC: Could not create DRI context.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->resources = context_drmContext;
for (i = 0; i < VIA_MAX_RENDSURF; ++i) {
pViaXvMC->rendSurf[i] = 0;
}
pViaXvMC->lastSrfDisplaying = ~0;
setupAttribDesc(display, port, &pViaXvMC->attrib, pViaXvMC->attribDesc);
pViaXvMC->hwLock = (drmLockPtr) pViaXvMC->sAreaAddress;
defaultQMatrices(pViaXvMC);
pViaXvMC->chromaIntraLoaded = 1;
pViaXvMC->chromaNonIntraLoaded = 1;
pViaXvMC->yStride = (width + 31) & ~31;
pViaXvMC->haveDecoder = 0;
pViaXvMC->attribChanged = 1;
pViaXvMC->haveXv = 0;
pViaXvMC->port = context->port;
pthread_mutex_init(&pViaXvMC->ctxMutex, NULL);
pViaXvMC->resources = context_mutex;
pViaXvMC->timeStamp = 0;
setRegion(0, 0, -1, -1, pViaXvMC->sRegion);
setRegion(0, 0, -1, -1, pViaXvMC->dRegion);
if (NULL == (pViaXvMC->xl =
initXvMCLowLevel(pViaXvMC->fd, &pViaXvMC->drmcontext,
pViaXvMC->hwLock, pViaXvMC->mmioAddress,
pViaXvMC->fbAddress, pViaXvMC->stride, pViaXvMC->depth,
context->width, context->height,
pViaXvMC->useAGP, pViaXvMC->chipId))) {
fprintf(stderr, "ViaXvMC: Could not allocate timestamp blit area.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->resources = context_lowLevel;
setAGPSyncLowLevel(pViaXvMC->xl, 1, 0);
if (NULL == (pViaXvMC->drawHash = drmHashCreate())) {
fprintf(stderr, "ViaXvMC: Could not allocate drawable hash table.\n");
return releaseContextResources(display, context, 1, BadAlloc);
}
pViaXvMC->resources = context_drawHash;
if (numContexts == 1) {
hwlLock(pViaXvMC->xl, 1);
setLowLevelLocking(pViaXvMC->xl, 0);
viaVideoSubPictureOffLocked(pViaXvMC->xl);
flushXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
}
return Success;
}
_X_EXPORT Status
XvMCDestroyContext(Display * display, XvMCContext * context)
{
ViaXvMCContext *pViaXvMC;
if (context == NULL) {
return (error_base + XvMCBadContext);
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
/*
* Release decoder if we have it. In case of crash or termination
* before XvMCDestroyContext, the X server will take care of this.
*/
releaseAttribDesc(pViaXvMC->attrib.numAttr, pViaXvMC->attribDesc);
releaseDecoder(pViaXvMC, 1);
return releaseContextResources(display, context, 1, Success);
}
_X_EXPORT Status
XvMCCreateSurface(Display * display, XvMCContext * context,
XvMCSurface * surface)
{
ViaXvMCContext *pViaXvMC;
ViaXvMCSurface *pViaSurface;
int priv_count;
unsigned *priv_data;
unsigned i;
Status ret;
if ((surface == NULL) || (context == NULL) || (display == NULL)) {
return BadValue;
}
pViaXvMC = (ViaXvMCContext *) context->privData;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (pViaXvMC == NULL) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return (error_base + XvMCBadContext);
}
pViaSurface = surface->privData =
(ViaXvMCSurface *) malloc(sizeof(ViaXvMCSurface));
if (!surface->privData) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAlloc;
}
XLockDisplay(display);
if ((ret = _xvmc_create_surface(display, context, surface,
&priv_count, &priv_data))) {
XUnlockDisplay(display);
free(pViaSurface);
fprintf(stderr, "Unable to create XvMC Surface.\n");
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
XUnlockDisplay(display);
pViaSurface->srfNo = priv_data[0];
/*
* Store framebuffer offsets to the buffers allocated for this surface.
* For some chipset revisions, surfaces may be double-buffered.
*/
pViaSurface->numBuffers = priv_data[1];
for (i = 0; i < pViaSurface->numBuffers; ++i) {
pViaSurface->offsets[i] = priv_data[i + 2];
}
pViaSurface->curBuf = 0;
/* Free data returned from xvmc_create_surface */
XFree(priv_data);
pViaSurface->width = context->width;
pViaSurface->height = context->height;
pViaSurface->yStride = pViaXvMC->yStride;
pViaSurface->privContext = pViaXvMC;
pViaSurface->privSubPic = NULL;
pViaSurface->needsSync = 0;
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCDestroySurface(Display * display, XvMCSurface * surface)
{
ViaXvMCSurface *pViaSurface;
if ((display == NULL) || (surface == NULL)) {
return BadValue;
}
if (surface->privData == NULL) {
return (error_base + XvMCBadSurface);
}
pViaSurface = (ViaXvMCSurface *) surface->privData;
XLockDisplay(display);
_xvmc_destroy_surface(display, surface);
XUnlockDisplay(display);
free(pViaSurface);
surface->privData = NULL;
return Success;
}
_X_EXPORT Status
XvMCPutSlice2(Display * display, XvMCContext * context, char *slice,
int nBytes, int sliceCode)
{
ViaXvMCContext *pViaXvMC;
CARD32 sCode = 0x00010000 | (sliceCode & 0xFF) << 24;
if ((display == NULL) || (context == NULL)) {
return BadValue;
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (!pViaXvMC->haveDecoder) {
fprintf(stderr, "XvMCPutSlice: This context does not own decoder!\n");
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAlloc;
}
viaMpegWriteSlice(pViaXvMC->xl, (CARD8 *) slice, nBytes, sCode);
flushPCIXvMCLowLevel(pViaXvMC->xl);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCPutSlice(Display * display, XvMCContext * context, char *slice,
int nBytes)
{
ViaXvMCContext *pViaXvMC;
if ((display == NULL) || (context == NULL)) {
return BadValue;
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (!pViaXvMC->haveDecoder) {
fprintf(stderr, "XvMCPutSlice: This context does not own decoder!\n");
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAlloc;
}
viaMpegWriteSlice(pViaXvMC->xl, (CARD8 *) slice, nBytes, 0);
flushPCIXvMCLowLevel(pViaXvMC->xl);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
static Status
updateXVOverlay(Display * display, ViaXvMCContext * pViaXvMC,
ViaXvMCSurface * pViaSurface, Drawable draw,
short srcx, short srcy, unsigned short srcw,
unsigned short srch, short destx, short desty,
unsigned short destw, unsigned short desth)
{
ViaXvMCCommandBuffer buf;
ViaXvMCSubPicture *pViaSubPic;
Status ret;
if (!pViaXvMC->haveXv) {
pViaXvMC->xvImage =
XvCreateImage(display, pViaXvMC->port, FOURCC_XVMC,
(char *)&buf, pViaSurface->width, pViaSurface->height);
pViaXvMC->gc = XCreateGC(display, draw, 0, 0);
pViaXvMC->haveXv = 1;
}
pViaXvMC->draw = draw;
pViaXvMC->xvImage->data = (char *)&buf;
buf.command = (pViaXvMC->attribChanged) ?
VIA_XVMC_COMMAND_FDISPLAY : VIA_XVMC_COMMAND_DISPLAY;
buf.ctxNo = pViaXvMC->ctxNo | VIA_XVMC_VALID;
buf.srfNo = pViaSurface->srfNo | VIA_XVMC_VALID;
pViaSubPic = pViaSurface->privSubPic;
buf.subPicNo = ((NULL == pViaSubPic) ? 0 : pViaSubPic->srfNo)
| VIA_XVMC_VALID;
buf.attrib = pViaXvMC->attrib;
XLockDisplay(display);
if ((ret = XvPutImage(display, pViaXvMC->port, draw, pViaXvMC->gc,
pViaXvMC->xvImage, srcx, srcy, srcw, srch,
destx, desty, destw, desth))) {
XUnlockDisplay(display);
return ret;
}
XSync(display, 0);
XUnlockDisplay(display);
pViaXvMC->attribChanged = 0;
return Success;
}
_X_EXPORT Status
XvMCPutSurface(Display * display, XvMCSurface * surface, Drawable draw,
short srcx, short srcy, unsigned short srcw,
unsigned short srch, short destx, short desty,
unsigned short destw, unsigned short desth, int flags)
{
/*
* This function contains some hairy locking logic. What we really want to
* do is to flip the picture ASAP, to get a low latency and smooth playback.
* However, if somebody else used the overlay since we used it last or if it is
* our first time, we'll have to call X to update the overlay first. Otherwise
* we'll do the overlay update once we've flipped. Since we release the hardware
* lock when we call X, X needs to verify using the SAREA that nobody else flipped
* in a picture between the lock release and the X server control. Similarly
* when the overlay update returns, we have to make sure that we still own the
* overlay.
*/
ViaXvMCSurface *pViaSurface;
ViaXvMCContext *pViaXvMC;
ViaXvMCSubPicture *pViaSubPic;
volatile ViaXvMCSAreaPriv *sAPriv;
Status ret;
unsigned dispSurface, lastSurface;
int overlayUpdated;
drawableInfo *drawInfo;
XvMCRegion sReg, dReg;
Bool forceUpdate = FALSE;
if ((display == NULL) || (surface == NULL)) {
return BadValue;
}
if (NULL == (pViaSurface = surface->privData)) {
return (error_base + XvMCBadSurface);
}
if (NULL == (pViaXvMC = pViaSurface->privContext)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
pViaSubPic = pViaSurface->privSubPic;
sAPriv = SAREAPTR(pViaXvMC);
setRegion(srcx, srcy, srcw, srch, sReg);
setRegion(destx, desty, destw, desth, dReg);
if ((!regionEqual(sReg, pViaXvMC->sRegion)) ||
(!regionEqual(dReg, pViaXvMC->dRegion))) {
/*
* Force update of the video overlay to match the new format.
*/
pViaXvMC->sRegion = sReg;
pViaXvMC->dRegion = dReg;
forceUpdate = TRUE;
}
hwlLock(pViaXvMC->xl, 1);
if (getDRIDrawableInfoLocked(pViaXvMC->drawHash, display,
pViaXvMC->screen, draw, 0, pViaXvMC->fd, pViaXvMC->drmcontext,
pViaXvMC->sAreaAddress, FALSE, &drawInfo, sizeof(*drawInfo))) {
hwlUnlock(pViaXvMC->xl, 1);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAccess;
}
setLowLevelLocking(pViaXvMC->xl, 0);
/*
* Put a surface ID in the SAREA to "authenticate" to the
* X server.
*/
dispSurface = sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort];
lastSurface = pViaXvMC->lastSrfDisplaying;
sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort] =
pViaXvMC->lastSrfDisplaying = pViaSurface->srfNo | VIA_XVMC_VALID;
overlayUpdated = 0;
viaVideoSetSWFLipLocked(pViaXvMC->xl, yOffs(pViaSurface),
uOffs(pViaSurface), vOffs(pViaSurface), pViaSurface->yStride,
pViaSurface->yStride >> 1);
while ((lastSurface != dispSurface) || forceUpdate) {
forceUpdate = FALSE;
flushPCIXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
/*
* We weren't the last to display. Update the overlay before flipping.
*/
ret =
updateXVOverlay(display, pViaXvMC, pViaSurface, draw, srcx, srcy,
srcw, srch, destx, desty, destw, desth);
if (ret) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
hwlLock(pViaXvMC->xl, 1);
if (getDRIDrawableInfoLocked(pViaXvMC->drawHash, display,
pViaXvMC->screen, draw, 0, pViaXvMC->fd, pViaXvMC->drmcontext,
pViaXvMC->sAreaAddress, FALSE, &drawInfo,
sizeof(*drawInfo))) {
hwlUnlock(pViaXvMC->xl, 1);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAccess;
}
setLowLevelLocking(pViaXvMC->xl, 0);
lastSurface = pViaSurface->srfNo | VIA_XVMC_VALID;
dispSurface = sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort];
overlayUpdated = 1;
}
/*
* Subpictures
*/
if (NULL != pViaSubPic) {
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort]
!= (pViaSubPic->srfNo | VIA_XVMC_VALID)) {
sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] =
pViaSubPic->srfNo | VIA_XVMC_VALID;
viaVideoSubPictureLocked(pViaXvMC->xl, pViaSubPic);
}
} else {
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] & VIA_XVMC_VALID) {
viaVideoSubPictureOffLocked(pViaXvMC->xl);
sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] &= ~VIA_XVMC_VALID;
}
}
/*
* Flip
*/
viaVideoSWFlipLocked(pViaXvMC->xl, flags,
pViaSurface->progressiveSequence);
flushXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
if (overlayUpdated || !drawInfo->touched) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
/*
* Update overlay
*/
ret =
updateXVOverlay(display, pViaXvMC, pViaSurface, draw, srcx, srcy,
srcw, srch, destx, desty, destw, desth);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
void
debugControl(const XvMCMpegControl * control)
{
printf("BVMV_range: %u\n", control->BVMV_range);
printf("BHMV_range: %u\n", control->BHMV_range);
printf("FVMV_range: %u\n", control->FVMV_range);
printf("FHMV_range: %u\n", control->FHMV_range);
printf("picture_structure: %u\n", control->picture_structure);
printf("intra_dc_precision: %u\n", control->intra_dc_precision);
printf("picture_coding_type: %u\n", control->picture_coding_type);
printf("mpeg_coding: %u\n", control->mpeg_coding);
printf("flags: 0x%x\n", control->flags);
}
_X_EXPORT Status
XvMCBeginSurface(Display * display,
XvMCContext * context,
XvMCSurface * target_surface,
XvMCSurface * past_surface,
XvMCSurface * future_surface, const XvMCMpegControl * control)
{
ViaXvMCSurface *targS, *futS, *pastS;
ViaXvMCContext *pViaXvMC;
int hadDecoderLast;
CARD32 timeStamp;
if ((display == NULL) || (context == NULL) || (target_surface == NULL)) {
return BadValue;
}
pViaXvMC = context->privData;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (grabDecoder(pViaXvMC, &hadDecoderLast)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAlloc;
}
pViaXvMC->haveDecoder = 1;
/*
* We need to wait for decoder idle at next flush, since hardware doesn't queue
* beginsurface requests until the decoder is idle. This is
* done by waiting on the last previous timestamp, or if there was another context
* having the decoder before us, by emitting a new one.
*/
if (pViaXvMC->useAGP) {
if (!hadDecoderLast || pViaXvMC->timeStamp == 0) {
timeStamp = viaDMATimeStampLowLevel(pViaXvMC->xl);
if (flushXvMCLowLevel(pViaXvMC->xl)) {
releaseDecoder(pViaXvMC, 0);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadAlloc;
}
pViaXvMC->timeStamp = timeStamp;
} else {
timeStamp = pViaXvMC->timeStamp;
}
setAGPSyncLowLevel(pViaXvMC->xl, 1, timeStamp);
}
if (!hadDecoderLast || !pViaXvMC->decoderOn) {
pViaXvMC->intraLoaded = 0;
pViaXvMC->nonIntraLoaded = 0;
}
viaMpegReset(pViaXvMC->xl);
targS = (ViaXvMCSurface *) target_surface->privData;
futS = NULL;
pastS = NULL;
pViaXvMC->rendSurf[0] = targS->srfNo | VIA_XVMC_VALID;
if (future_surface) {
futS = (ViaXvMCSurface *) future_surface->privData;
futS->needsSync = 0;
}
if (past_surface) {
pastS = (ViaXvMCSurface *) past_surface->privData;
pastS->needsSync = 0;
}
targS->progressiveSequence = (control->flags & XVMC_PROGRESSIVE_SEQUENCE);
targS->topFieldFirst = (control->flags & XVMC_TOP_FIELD_FIRST);
targS->privSubPic = NULL;
viaMpegSetSurfaceStride(pViaXvMC->xl, pViaXvMC);
viaMpegSetFB(pViaXvMC->xl, 0, yOffs(targS), uOffs(targS), vOffs(targS));
if (past_surface) {
viaMpegSetFB(pViaXvMC->xl, 1, yOffs(pastS), uOffs(pastS),
vOffs(pastS));
} else {
viaMpegSetFB(pViaXvMC->xl, 1, 0, 0, 0);
}
if (future_surface) {
viaMpegSetFB(pViaXvMC->xl, 2, yOffs(futS), uOffs(futS), vOffs(futS));
} else {
viaMpegSetFB(pViaXvMC->xl, 2, 0, 0, 0);
}
viaMpegBeginPicture(pViaXvMC->xl, pViaXvMC, context->width,
context->height, control);
flushPCIXvMCLowLevel(pViaXvMC->xl);
targS->needsSync = 1;
targS->syncMode = LL_MODE_DECODER_IDLE;
pViaXvMC->decoderOn = 1;
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCSyncSurface(Display * display, XvMCSurface * surface)
{
ViaXvMCSurface *pViaSurface;
ViaXvMCContext *pViaXvMC;
unsigned i;
if ((display == NULL) || (surface == NULL)) {
return BadValue;
}
if (surface->privData == NULL) {
return (error_base + XvMCBadSurface);
}
pViaSurface = (ViaXvMCSurface *) surface->privData;
pViaXvMC = pViaSurface->privContext;
if (pViaXvMC == NULL) {
return (error_base + XvMCBadSurface);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (pViaSurface->needsSync) {
CARD32 timeStamp = pViaSurface->timeStamp;
int syncMode = pViaSurface->syncMode;
if (pViaXvMC->useAGP) {
syncMode = (pViaSurface->syncMode == LL_MODE_2D ||
pViaSurface->timeStamp < pViaXvMC->timeStamp) ?
LL_MODE_2D : LL_MODE_DECODER_IDLE;
if (pViaSurface->syncMode != LL_MODE_2D)
timeStamp = pViaXvMC->timeStamp;
} else if (syncMode != LL_MODE_2D &&
pViaXvMC->rendSurf[0] != (pViaSurface->srfNo | VIA_XVMC_VALID)) {
pViaSurface->needsSync = 0;
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
if (syncXvMCLowLevel(pViaXvMC->xl, syncMode, 1,
pViaSurface->timeStamp)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadValue;
}
pViaSurface->needsSync = 0;
}
if (pViaXvMC->rendSurf[0] == (pViaSurface->srfNo | VIA_XVMC_VALID)) {
pViaSurface->needsSync = 0;
for (i = 0; i < VIA_MAX_RENDSURF; ++i) {
pViaXvMC->rendSurf[i] = 0;
}
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCLoadQMatrix(Display * display, XvMCContext * context,
const XvMCQMatrix * qmx)
{
ViaXvMCContext * pViaXvMC;
if ((display == NULL) || (context == NULL)) {
return BadValue;
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (qmx->load_intra_quantiser_matrix) {
memcpy(pViaXvMC->intra_quantiser_matrix,
qmx->intra_quantiser_matrix, sizeof(qmx->intra_quantiser_matrix));
pViaXvMC->intraLoaded = 0;
}
if (qmx->load_non_intra_quantiser_matrix) {
memcpy(pViaXvMC->non_intra_quantiser_matrix,
qmx->non_intra_quantiser_matrix,
sizeof(qmx->non_intra_quantiser_matrix));
pViaXvMC->nonIntraLoaded = 0;
}
if (qmx->load_chroma_intra_quantiser_matrix) {
memcpy(pViaXvMC->chroma_intra_quantiser_matrix,
qmx->chroma_intra_quantiser_matrix,
sizeof(qmx->chroma_intra_quantiser_matrix));
pViaXvMC->chromaIntraLoaded = 0;
}
if (qmx->load_chroma_non_intra_quantiser_matrix) {
memcpy(pViaXvMC->chroma_non_intra_quantiser_matrix,
qmx->chroma_non_intra_quantiser_matrix,
sizeof(qmx->chroma_non_intra_quantiser_matrix));
pViaXvMC->chromaNonIntraLoaded = 0;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
/*
* Below, we provide functions unusable for this implementation, but for
* standard completeness.
*/
_X_EXPORT Status XvMCRenderSurface
(Display * display,
XvMCContext * context,
unsigned int picture_structure,
XvMCSurface * target_surface,
XvMCSurface * past_surface,
XvMCSurface * future_surface,
unsigned int flags,
unsigned int num_macroblocks,
unsigned int first_macroblock,
XvMCMacroBlockArray * macroblock_array, XvMCBlockArray * blocks)
{
return (error_base + XvMCBadContext);
}
_X_EXPORT Status XvMCCreateBlocks
(Display * display,
XvMCContext * context, unsigned int num_blocks, XvMCBlockArray * block)
{
return (error_base + XvMCBadContext);
}
_X_EXPORT Status
XvMCDestroyBlocks(Display * display, XvMCBlockArray * block)
{
return Success;
}
_X_EXPORT Status XvMCCreateMacroBlocks
(Display * display,
XvMCContext * context,
unsigned int num_blocks, XvMCMacroBlockArray * blocks)
{
return (error_base + XvMCBadContext);
}
_X_EXPORT Status
XvMCDestroyMacroBlocks(Display * display, XvMCMacroBlockArray * block)
{
return (error_base + XvMCBadContext);
}
_X_EXPORT Status
XvMCCreateSubpicture(Display * display,
XvMCContext * context,
XvMCSubpicture * subpicture,
unsigned short width, unsigned short height, int xvimage_id)
{
ViaXvMCContext *pViaXvMC;
ViaXvMCSubPicture *pViaSubPic;
int priv_count;
unsigned *priv_data;
Status ret;
if ((subpicture == NULL) || (context == NULL) || (display == NULL)) {
return BadValue;
}
pViaXvMC = (ViaXvMCContext *) context->privData;
if (pViaXvMC == NULL) {
return (error_base + XvMCBadContext);
}
subpicture->privData = (ViaXvMCSubPicture *)
malloc(sizeof(ViaXvMCSubPicture));
if (!subpicture->privData) {
return BadAlloc;
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
subpicture->width = context->width;
subpicture->height = context->height;
subpicture->xvimage_id = xvimage_id;
pViaSubPic = (ViaXvMCSubPicture *) subpicture->privData;
XLockDisplay(display);
if ((ret = _xvmc_create_subpicture(display, context, subpicture,
&priv_count, &priv_data))) {
XUnlockDisplay(display);
free(pViaSubPic);
fprintf(stderr, "Unable to create XvMC Subpicture.\n");
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
XUnlockDisplay(display);
subpicture->num_palette_entries = VIA_SUBPIC_PALETTE_SIZE;
subpicture->entry_bytes = 3;
strncpy(subpicture->component_order, "YUV", 4);
pViaSubPic->srfNo = priv_data[0];
pViaSubPic->offset = priv_data[1];
pViaSubPic->stride = (subpicture->width + 31) & ~31;
pViaSubPic->privContext = pViaXvMC;
pViaSubPic->ia44 = (xvimage_id == FOURCC_IA44);
pViaSubPic->needsSync = 0;
/* Free data returned from _xvmc_create_subpicture */
XFree(priv_data);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCSetSubpicturePalette(Display * display, XvMCSubpicture * subpicture,
unsigned char *palette)
{
ViaXvMCSubPicture *pViaSubPic;
ViaXvMCContext *pViaXvMC;
volatile ViaXvMCSAreaPriv *sAPriv;
unsigned i;
CARD32 tmp;
if ((subpicture == NULL) || (display == NULL)) {
return BadValue;
}
if (subpicture->privData == NULL) {
return (error_base + XvMCBadSubpicture);
}
pViaSubPic = (ViaXvMCSubPicture *) subpicture->privData;
for (i = 0; i < VIA_SUBPIC_PALETTE_SIZE; ++i) {
tmp = *palette++ << 8;
tmp |= *palette++ << 16;
tmp |= *palette++ << 24;
tmp |= ((i & 0x0f) << 4) | 0x07;
pViaSubPic->palette[i] = tmp;
}
pViaXvMC = pViaSubPic->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
sAPriv = SAREAPTR(pViaXvMC);
hwlLock(pViaXvMC->xl, 1);
setLowLevelLocking(pViaXvMC->xl, 0);
/*
* If the subpicture is displaying, Immeadiately update it with the
* new palette.
*/
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] ==
(pViaSubPic->srfNo | VIA_XVMC_VALID)) {
viaVideoSubPictureLocked(pViaXvMC->xl, pViaSubPic);
}
flushPCIXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
static int
findOverlap(unsigned width, unsigned height,
short *dstX, short *dstY,
short *srcX, short *srcY, unsigned short *areaW, unsigned short *areaH)
{
int w, h;
unsigned mWidth, mHeight;
w = *areaW;
h = *areaH;
if ((*dstX >= width) || (*dstY >= height))
return 1;
if (*dstX < 0) {
w += *dstX;
*srcX -= *dstX;
*dstX = 0;
}
if (*dstY < 0) {
h += *dstY;
*srcY -= *dstY;
*dstY = 0;
}
if ((w <= 0) || ((h <= 0)))
return 1;
mWidth = width - *dstX;
mHeight = height - *dstY;
*areaW = (w <= mWidth) ? w : mWidth;
*areaH = (h <= mHeight) ? h : mHeight;
return 0;
}
_X_EXPORT Status
XvMCClearSubpicture(Display * display,
XvMCSubpicture * subpicture,
short x,
short y, unsigned short width, unsigned short height, unsigned int color)
{
ViaXvMCContext *pViaXvMC;
ViaXvMCSubPicture *pViaSubPic;
short dummyX, dummyY;
unsigned long bOffs;
if ((subpicture == NULL) || (display == NULL)) {
return BadValue;
}
if (subpicture->privData == NULL) {
return (error_base + XvMCBadSubpicture);
}
pViaSubPic = (ViaXvMCSubPicture *) subpicture->privData;
pViaXvMC = pViaSubPic->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
/* Clip clearing area so that it fits inside subpicture. */
if (findOverlap(subpicture->width, subpicture->height, &x, &y,
&dummyX, &dummyY, &width, &height)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
bOffs = pViaSubPic->offset + y * pViaSubPic->stride + x;
viaBlit(pViaXvMC->xl, 8, 0, pViaSubPic->stride, bOffs, pViaSubPic->stride,
width, height, 1, 1, VIABLIT_FILL, color);
pViaSubPic->needsSync = 1;
pViaSubPic->timeStamp = viaDMATimeStampLowLevel(pViaXvMC->xl);
if (flushXvMCLowLevel(pViaXvMC->xl)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadValue;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCCompositeSubpicture(Display * display,
XvMCSubpicture * subpicture,
XvImage * image,
short srcx,
short srcy,
unsigned short width, unsigned short height, short dstx, short dsty)
{
unsigned i;
ViaXvMCContext *pViaXvMC;
ViaXvMCSubPicture *pViaSubPic;
CARD8 *dAddr, *sAddr;
if ((subpicture == NULL) || (display == NULL) || (image == NULL)) {
return BadValue;
}
if (NULL == (pViaSubPic = (ViaXvMCSubPicture *) subpicture->privData)) {
return (error_base + XvMCBadSubpicture);
}
pViaXvMC = pViaSubPic->privContext;
if (image->id != subpicture->xvimage_id)
return BadMatch;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
/*
* Clip copy area so that it fits inside subpicture and image.
*/
if (findOverlap(subpicture->width, subpicture->height,
&dstx, &dsty, &srcx, &srcy, &width, &height)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
if (findOverlap(image->width, image->height,
&srcx, &srcy, &dstx, &dsty, &width, &height)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
if (pViaSubPic->needsSync) {
if (syncXvMCLowLevel(pViaXvMC->xl, LL_MODE_2D, 0,
pViaSubPic->timeStamp)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadValue;
}
pViaSubPic->needsSync = 0;
}
for (i = 0; i < height; ++i) {
dAddr = (((CARD8 *) pViaXvMC->fbAddress) +
(pViaSubPic->offset + (dsty + i) * pViaSubPic->stride + dstx));
sAddr = (((CARD8 *) image->data) +
(image->offsets[0] + (srcy + i) * image->pitches[0] + srcx));
memcpy(dAddr, sAddr, width);
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCBlendSubpicture(Display * display,
XvMCSurface * target_surface,
XvMCSubpicture * subpicture,
short subx,
short suby,
unsigned short subw,
unsigned short subh,
short surfx, short surfy, unsigned short surfw, unsigned short surfh)
{
ViaXvMCSurface *pViaSurface;
ViaXvMCSubPicture *pViaSubPic;
if ((display == NULL) || target_surface == NULL) {
return BadValue;
}
if (subx || suby || surfx || surfy || (subw != surfw) || (subh != surfh)) {
fprintf(stderr, "ViaXvMC: Only completely overlapping subpicture "
"supported.\n");
return BadValue;
}
if (NULL == (pViaSurface = target_surface->privData)) {
return (error_base + XvMCBadSurface);
}
if (subpicture) {
if (NULL == (pViaSubPic = subpicture->privData)) {
return (error_base + XvMCBadSubpicture);
}
pViaSurface->privSubPic = pViaSubPic;
} else {
pViaSurface->privSubPic = NULL;
}
return Success;
}
_X_EXPORT Status
XvMCBlendSubpicture2(Display * display,
XvMCSurface * source_surface,
XvMCSurface * target_surface,
XvMCSubpicture * subpicture,
short subx,
short suby,
unsigned short subw,
unsigned short subh,
short surfx, short surfy, unsigned short surfw, unsigned short surfh)
{
ViaXvMCSurface *pViaSurface, *pViaSSurface;
ViaXvMCSubPicture *pViaSubPic;
ViaXvMCContext *pViaXvMC;
unsigned width, height;
if ((display == NULL) || target_surface == NULL || source_surface == NULL) {
return BadValue;
}
if (subx || suby || surfx || surfy || (subw != surfw) || (subh != surfh)) {
fprintf(stderr, "ViaXvMC: Only completely overlapping subpicture "
"supported.\n");
return BadMatch;
}
if (NULL == (pViaSurface = target_surface->privData)) {
return (error_base + XvMCBadSurface);
}
if (NULL == (pViaSSurface = source_surface->privData)) {
return (error_base + XvMCBadSurface);
}
pViaXvMC = pViaSurface->privContext;
width = pViaSSurface->width;
height = pViaSSurface->height;
if (width != pViaSurface->width || height != pViaSSurface->height) {
return BadMatch;
}
if (XvMCSyncSurface(display, source_surface)) {
return BadValue;
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
viaBlit(pViaXvMC->xl, 8, yOffs(pViaSSurface), pViaSSurface->yStride,
yOffs(pViaSurface), pViaSurface->yStride,
width, height, 1, 1, VIABLIT_COPY, 0);
flushPCIXvMCLowLevel(pViaXvMC->xl);
if (pViaXvMC->chipId != PCI_CHIP_VT3259) {
/*
* YV12 Chroma blit.
*/
viaBlit(pViaXvMC->xl, 8, uOffs(pViaSSurface),
pViaSSurface->yStride >> 1, uOffs(pViaSurface),
pViaSurface->yStride >> 1, width >> 1, height >> 1, 1, 1,
VIABLIT_COPY, 0);
flushPCIXvMCLowLevel(pViaXvMC->xl);
viaBlit(pViaXvMC->xl, 8, vOffs(pViaSSurface),
pViaSSurface->yStride >> 1, vOffs(pViaSurface),
pViaSurface->yStride >> 1, width >> 1, height >> 1, 1, 1,
VIABLIT_COPY, 0);
} else {
/*
* NV12 Chroma blit.
*/
viaBlit(pViaXvMC->xl, 8, vOffs(pViaSSurface), pViaSSurface->yStride,
vOffs(pViaSurface), pViaSurface->yStride,
width, height >> 1, 1, 1, VIABLIT_COPY, 0);
}
pViaSurface->needsSync = 1;
pViaSurface->syncMode = LL_MODE_2D;
pViaSurface->timeStamp = viaDMATimeStampLowLevel(pViaXvMC->xl);
if (flushXvMCLowLevel(pViaXvMC->xl)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadValue;
}
if (subpicture) {
if (NULL == (pViaSubPic = subpicture->privData)) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return (error_base + XvMCBadSubpicture);
}
pViaSurface->privSubPic = pViaSubPic;
} else {
pViaSurface->privSubPic = NULL;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCSyncSubpicture(Display * display, XvMCSubpicture * subpicture)
{
ViaXvMCSubPicture *pViaSubPic;
ViaXvMCContext *pViaXvMC;
Status retVal = 0;
if ((display == NULL) || subpicture == NULL) {
return BadValue;
}
if (NULL == (pViaSubPic = subpicture->privData)) {
return (error_base + XvMCBadSubpicture);
}
pViaXvMC = pViaSubPic->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (pViaSubPic->needsSync) {
if (syncXvMCLowLevel(pViaXvMC->xl, LL_MODE_2D,
0, pViaSubPic->timeStamp)) {
retVal = BadValue;
}
pViaSubPic->needsSync = 0;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return retVal;
}
_X_EXPORT Status
XvMCFlushSubpicture(Display * display, XvMCSubpicture * subpicture)
{
ViaXvMCSubPicture *pViaSubPic;
if ((display == NULL) || subpicture == NULL) {
return BadValue;
}
if (NULL == (pViaSubPic = subpicture->privData)) {
return (error_base + XvMCBadSubpicture);
}
return Success;
}
_X_EXPORT Status
XvMCDestroySubpicture(Display * display, XvMCSubpicture * subpicture)
{
ViaXvMCSubPicture *pViaSubPic;
ViaXvMCContext *pViaXvMC;
volatile ViaXvMCSAreaPriv *sAPriv;
if ((display == NULL) || subpicture == NULL) {
return BadValue;
}
if (NULL == (pViaSubPic = subpicture->privData)) {
return (error_base + XvMCBadSubpicture);
}
pViaXvMC = pViaSubPic->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
sAPriv = SAREAPTR(pViaXvMC);
hwlLock(pViaXvMC->xl, 1);
setLowLevelLocking(pViaXvMC->xl, 0);
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] ==
(pViaSubPic->srfNo | VIA_XVMC_VALID)) {
viaVideoSubPictureOffLocked(pViaXvMC->xl);
sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] = 0;
}
flushPCIXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
XLockDisplay(display);
_xvmc_destroy_subpicture(display, subpicture);
XUnlockDisplay(display);
free(pViaSubPic);
subpicture->privData = NULL;
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCGetSubpictureStatus(Display * display, XvMCSubpicture * subpic, int *stat)
{
ViaXvMCSubPicture *pViaSubPic;
ViaXvMCContext *pViaXvMC;
volatile ViaXvMCSAreaPriv *sAPriv;
if ((display == NULL) || subpic == NULL) {
return BadValue;
}
if (NULL == (pViaSubPic = subpic->privData)) {
return (error_base + XvMCBadSubpicture);
}
if (stat) {
*stat = 0;
pViaXvMC = pViaSubPic->privContext;
sAPriv = SAREAPTR(pViaXvMC);
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] ==
(pViaSubPic->srfNo | VIA_XVMC_VALID))
*stat |= XVMC_DISPLAYING;
}
return Success;
}
_X_EXPORT Status
XvMCFlushSurface(Display * display, XvMCSurface * surface)
{
ViaXvMCSurface *pViaSurface;
ViaXvMCContext *pViaXvMC;
Status ret;
if ((display == NULL) || surface == NULL) {
return BadValue;
}
if (NULL == (pViaSurface = surface->privData)) {
return (error_base + XvMCBadSurface);
}
pViaXvMC = pViaSurface->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (pViaSurface->needsSync)
pViaSurface->timeStamp = pViaXvMC->timeStamp =
viaDMATimeStampLowLevel(pViaXvMC->xl);
ret = (flushXvMCLowLevel(pViaXvMC->xl)) ? BadValue : Success;
if (pViaXvMC->rendSurf[0] == (pViaSurface->srfNo | VIA_XVMC_VALID)) {
hwlLock(pViaXvMC->xl, 0);
pViaXvMC->haveDecoder = 0;
releaseDecoder(pViaXvMC, 0);
hwlUnlock(pViaXvMC->xl, 0);
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
_X_EXPORT Status
XvMCGetSurfaceStatus(Display * display, XvMCSurface * surface, int *stat)
{
ViaXvMCSurface *pViaSurface;
ViaXvMCContext *pViaXvMC;
volatile ViaXvMCSAreaPriv *sAPriv;
unsigned i;
int ret = 0;
if ((display == NULL) || surface == NULL) {
return BadValue;
}
if (NULL == (pViaSurface = surface->privData)) {
return (error_base + XvMCBadSurface);
}
if (stat) {
*stat = 0;
pViaXvMC = pViaSurface->privContext;
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
sAPriv = SAREAPTR(pViaXvMC);
if (sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort]
== (pViaSurface->srfNo | VIA_XVMC_VALID))
*stat |= XVMC_DISPLAYING;
for (i = 0; i < VIA_MAX_RENDSURF; ++i) {
if (pViaXvMC->rendSurf[i] ==
(pViaSurface->srfNo | VIA_XVMC_VALID)) {
*stat |= XVMC_RENDERING;
break;
}
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
}
return ret;
}
_X_EXPORT XvAttribute *
XvMCQueryAttributes(Display * display, XvMCContext * context, int *number)
{
ViaXvMCContext *pViaXvMC;
XvAttribute *ret;
unsigned long siz;
*number = 0;
if ((display == NULL) || (context == NULL)) {
return NULL;
}
if (NULL == (pViaXvMC = context->privData)) {
return NULL;
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (NULL != (ret = (XvAttribute *)
malloc(siz = VIA_NUM_XVMC_ATTRIBUTES * sizeof(XvAttribute)))) {
memcpy(ret, pViaXvMC->attribDesc, siz);
*number = VIA_NUM_XVMC_ATTRIBUTES;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
_X_EXPORT Status
XvMCSetAttribute(Display * display,
XvMCContext * context, Atom attribute, int value)
{
int found;
unsigned i;
ViaXvMCContext *pViaXvMC;
ViaXvMCCommandBuffer buf;
if ((display == NULL) || (context == NULL)) {
return (error_base + XvMCBadContext);
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
found = 0;
for (i = 0; i < pViaXvMC->attrib.numAttr; ++i) {
if (attribute == pViaXvMC->attrib.attributes[i].attribute) {
if ((!(pViaXvMC->attribDesc[i].flags & XvSettable)) ||
value < pViaXvMC->attribDesc[i].min_value ||
value > pViaXvMC->attribDesc[i].max_value) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadValue;
}
pViaXvMC->attrib.attributes[i].value = value;
found = 1;
pViaXvMC->attribChanged = 1;
break;
}
}
if (!found) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return BadMatch;
}
if (pViaXvMC->haveXv) {
buf.command = VIA_XVMC_COMMAND_ATTRIBUTES;
pViaXvMC->xvImage->data = (char *)&buf;
buf.ctxNo = pViaXvMC->ctxNo | VIA_XVMC_VALID;
buf.attrib = pViaXvMC->attrib;
XLockDisplay(display);
pViaXvMC->attribChanged =
XvPutImage(display, pViaXvMC->port, pViaXvMC->draw,
pViaXvMC->gc, pViaXvMC->xvImage, 0, 0, 1, 1, 0, 0, 1, 1);
XUnlockDisplay(display);
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
_X_EXPORT Status
XvMCGetAttribute(Display * display,
XvMCContext * context, Atom attribute, int *value)
{
int found;
unsigned i;
ViaXvMCContext *pViaXvMC;
if ((display == NULL) || (context == NULL)) {
return (error_base + XvMCBadContext);
}
if (NULL == (pViaXvMC = context->privData)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
found = 0;
for (i = 0; i < pViaXvMC->attrib.numAttr; ++i) {
if (attribute == pViaXvMC->attrib.attributes[i].attribute) {
if (pViaXvMC->attribDesc[i].flags & XvGettable) {
*value = pViaXvMC->attrib.attributes[i].value;
found = 1;
break;
}
}
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
if (!found)
return BadMatch;
return Success;
}
_X_EXPORT Status
XvMCHideSurface(Display * display, XvMCSurface * surface)
{
ViaXvMCSurface *pViaSurface;
ViaXvMCContext *pViaXvMC;
ViaXvMCSubPicture *pViaSubPic;
volatile ViaXvMCSAreaPriv *sAPriv;
ViaXvMCCommandBuffer buf;
Status ret;
if ((display == NULL) || (surface == NULL)) {
return BadValue;
}
if (NULL == (pViaSurface = surface->privData)) {
return (error_base + XvMCBadSurface);
}
if (NULL == (pViaXvMC = pViaSurface->privContext)) {
return (error_base + XvMCBadContext);
}
ppthread_mutex_lock(&pViaXvMC->ctxMutex);
if (!pViaXvMC->haveXv) {
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
sAPriv = SAREAPTR(pViaXvMC);
hwlLock(pViaXvMC->xl, 1);
if (sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort] !=
(pViaSurface->srfNo | VIA_XVMC_VALID)) {
hwlUnlock(pViaXvMC->xl, 1);
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}
setLowLevelLocking(pViaXvMC->xl, 0);
if (NULL != (pViaSubPic = pViaSurface->privSubPic)) {
if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] ==
(pViaSubPic->srfNo | VIA_XVMC_VALID)) {
sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] &= ~VIA_XVMC_VALID;
viaVideoSubPictureOffLocked(pViaXvMC->xl);
}
}
flushPCIXvMCLowLevel(pViaXvMC->xl);
setLowLevelLocking(pViaXvMC->xl, 1);
hwlUnlock(pViaXvMC->xl, 1);
buf.command = VIA_XVMC_COMMAND_UNDISPLAY;
buf.ctxNo = pViaXvMC->ctxNo | VIA_XVMC_VALID;
buf.srfNo = pViaSurface->srfNo | VIA_XVMC_VALID;
pViaXvMC->xvImage->data = (char *)&buf;
if ((ret = XvPutImage(display, pViaXvMC->port, pViaXvMC->draw,
pViaXvMC->gc, pViaXvMC->xvImage, 0, 0, 1, 1, 0, 0, 1, 1))) {
fprintf(stderr, "XvMCPutSurface: Hiding overlay failed.\n");
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return ret;
}
ppthread_mutex_unlock(&pViaXvMC->ctxMutex);
return Success;
}