/***************************************************************************** * VIA Unichrome XvMC extension client lib. * * Copyright (c) 2004 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. *TODO: Picture copying support. *Change: 0.9.3, Bugfix by Pascal Brisset. *Change: 0.10.1, Support for interlaced streams Thanks to Pascal Brisset. */ #undef WAITPAUSE #include "viaXvMCPriv.h" #include #include #include #include #include #include #include #include #include "viaXvMC.h" #ifndef BEAM_HWMPEG #define VIA_XVMC_DECODERTIMEOUT 40000 /*(microseconds)*/ #define VIA_SLICEBUSYMASK 0x00000200 #define VIA_BUSYMASK 0x00000207 #define VIA_SLICEIDLEVAL 0x00000200 #define VIA_IDLEVAL 0x00000204 #endif #define SAREAPTR(ctx) ((ViaXvMCSAreaPriv *) \ (((CARD8 *)(ctx)->sAreaAddress) + \ (ctx)->sAreaPrivOffset)) static int error_base; static int event_base; #define FOURCC_VIA 0x4E4B4C57 #ifndef BEAM_HWMPEG static unsigned timeDiff(struct timeval *now,struct timeval *then) { return (now->tv_usec >= then->tv_usec) ? now->tv_usec - then->tv_usec : 1000000 - (then->tv_usec - now->tv_usec); } static CARD32 decoderWaitLocked(ViaXvMCContext *ctx,unsigned mode) { struct timespec sleep; #ifdef WAITPAUSE struct timespec rem; #endif struct timeval now,then; struct timezone here; CARD32 ret,busyMask,idleVal; sleep.tv_nsec = 1; sleep.tv_sec = 0; here.tz_minuteswest = 0; here.tz_dsttime = 0; gettimeofday(&then,&here); switch (mode) { case 1: busyMask = VIA_SLICEBUSYMASK; idleVal = VIA_SLICEIDLEVAL; break; default: busyMask = VIA_BUSYMASK; idleVal = VIA_IDLEVAL; break; } while (viaMpegIsBusyLocked(ctx,busyMask,idleVal)) { gettimeofday(&now,&here); if (timeDiff(&now,&then) > VIA_XVMC_DECODERTIMEOUT) { if (viaMpegIsBusyLocked(ctx,busyMask,idleVal)) { ctx->decTimeOut = 1; fprintf(stderr,"ViaXvMC: Decoder timed out.\n"); } break; } HW_UNLOCK(ctx); #ifdef WAITPAUSE sleep.tv_sec = 0; sleep.tv_nsec = 1; nanosleep(&sleep,&rem); #endif viaSleepRTC(ctx); HW_LOCK(ctx); } ret = viaMpegGetStatusLocked(ctx); return ret; } #endif 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); } #ifndef BEAM_HWMPEG 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; } #endif static void releaseDecoder(ViaXvMCContext *ctx,int clearCtx) { volatile ViaXvMCSAreaPriv *sAPriv; sAPriv = SAREAPTR(ctx); if ((XVMC_DECODER_FUTEX(sAPriv)->lock & ~DRM_LOCK_CONT) == (ctx->drmcontext | DRM_LOCK_HELD)) { DRM_CAS_RESULT(__ret); if (clearCtx) sAPriv->XvMCCtxNoGrabbed = ~0; DRM_CAS( XVMC_DECODER_FUTEX(sAPriv), ctx->drmcontext | DRM_LOCK_HELD, 0, __ret); if (__ret) { drm_via_futex_t fx; fx.op = VIA_FUTEX_WAKE; fx.lock = 0; XVMC_DECODER_FUTEX(sAPriv)->lock = 0; drmCommandWrite(ctx->fd, DRM_VIA_DEC_FUTEX, &fx, sizeof(fx)); } } } static int grabDecoder( ViaXvMCContext *ctx, int *hadLastLock) { volatile ViaXvMCSAreaPriv *sAPriv = SAREAPTR(ctx); int retFtx; /* * 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) return 0; while(1) { DRM_CAS_RESULT(__ret); DRM_CAS( XVMC_DECODER_FUTEX(sAPriv), 0, ctx->drmcontext | DRM_LOCK_HELD,__ret); if (__ret) { drm_via_futex_t fx; int lockVal; /* * The decoder is locked. Try to * mark the lock as contended and go to * sleep. */ lockVal = XVMC_DECODER_FUTEX(sAPriv)->lock; if (!(lockVal & DRM_LOCK_HELD)) continue; if ((lockVal & ~(DRM_LOCK_HELD | DRM_LOCK_CONT)) == ctx->drmcontext) { *hadLastLock = 1; return 0; } fx.val = lockVal | DRM_LOCK_CONT; DRM_CAS( XVMC_DECODER_FUTEX(sAPriv), lockVal, fx.val , __ret); lockVal = XVMC_DECODER_FUTEX(sAPriv)->lock; if (__ret) continue; fx.op = VIA_FUTEX_WAIT; fx.lock = 0; fx.ms = 10; if (0 != (retFtx = drmCommandWrite(ctx->fd,DRM_VIA_DEC_FUTEX, &fx, sizeof(fx)))) { switch(retFtx) { case -EBUSY: return 1; case -EINVAL: { /* * DRM does not support the futex IOCTL. Sleep. */ struct timespec sleep,rem; sleep.tv_nsec = 1; sleep.tv_sec = 0; nanosleep(&sleep,&rem); break; } default: break; } } } else { /* * The decoder is available. Mark it as used, check if we were * the one who had it locked last time and return. */ *hadLastLock = (sAPriv->XvMCCtxNoGrabbed == ctx->drmcontext); sAPriv->XvMCCtxNoGrabbed = ctx->drmcontext; return 0; } } /* * We should never get here. */ return 0; } static void setupAttribDesc(Display *display, XvPortID port, const ViaXvMCAttrHolder *attrib, XvAttribute attribDesc[]) { XvAttribute *XvAttribs,*curAD; int num; unsigned i,j; XvAttribs = XvQueryPortAttributes(display, port, &num); for(i=0; inumAttr; ++i) { curAD = attribDesc + i; curAD->flags = 0; curAD->min_value = 0; curAD->max_value = 0; curAD->name = NULL; for(j=0; jattributes[i].attribute == XInternAtom(display,XvAttribs[j].name,TRUE)) { *curAD = XvAttribs[j]; curAD->name = strdup(XvAttribs[j].name); break; } } } if (XvAttribs) XFree(XvAttribs); } static void releaseAttribDesc(int numAttr, XvAttribute attribDesc[]) { int i; for (i=0; isurface_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; /* Verify the XvMC extension exists */ if(! XvMCQueryExtension(display, &event_base, &error_base)) { fprintf(stderr,"XvMC Extension is not available!\n"); free(pViaXvMC); return BadAlloc; } /* Verify XvMC version */ ret = XvMCQueryVersion(display, &major, &minor); if(ret) { fprintf(stderr,"XvMCQuery Version Failed, unable to determine " "protocol version\n"); } /* FIXME: Check Major and Minor here */ /* Check for drm */ if(! drmAvailable()) { fprintf(stderr,"Direct Rendering is not avilable on this system!\n"); free(pViaXvMC); return BadAlloc; } if((pViaXvMC->fd = drmOpen("via",NULL)) < 0) { fprintf(stderr,"DRM Device for via could not be opened.\n"); free(pViaXvMC); return BadAccess; } /* * Get magic number and put it in "flags" for passing to the X server * that will authenticate us to the drm so that we will be able to * map memory and other stuff. */ drmGetMagic(pViaXvMC->fd,&magic); context->flags = (unsigned long)magic; /* * Pass control to the X server to create a drmContext for us and * validate the with/height and flags. */ if((ret = _xvmc_create_context(display, context, &priv_count, &priv_data))) { fprintf(stderr,"Unable to create XvMC Context.\n"); free(pViaXvMC); return ret; } if(priv_count != (sizeof(ViaXvMCCreateContextRec) >> 2)) { fprintf(stderr,"_xvmc_create_context() returned incorrect " "data size!\n"); fprintf(stderr,"\tExpected %d, got %d\n", (sizeof(ViaXvMCCreateContextRec) >> 2), priv_count); _xvmc_destroy_context(display, context); free(pViaXvMC); return BadAlloc; } tmpComm = ( ViaXvMCCreateContextRec *) priv_data; if ((tmpComm->major != VIAXVMC_MAJOR) || (tmpComm->minor != VIAXVMC_MINOR)) { fprintf(stderr,"Version mismatch between the XFree86 via driver\n" "and the XvMC library. Cannot continue!\n"); return BadAlloc; } pViaXvMC->ctxNo = tmpComm->ctxNo; pViaXvMC->drmcontext = tmpComm->drmcontext; pViaXvMC->fb_base = tmpComm->fbBase; pViaXvMC->fbOffset = tmpComm->fbOffset; pViaXvMC->fbSize = tmpComm->fbSize; pViaXvMC->mmioOffset = tmpComm->mmioOffset; pViaXvMC->mmioSize = tmpComm->mmioSize; pViaXvMC->sAreaOffset = tmpComm->sAreaOffset; pViaXvMC->sAreaSize = tmpComm->sAreaSize; pViaXvMC->sAreaPrivOffset = tmpComm->sAreaPrivOffset; pViaXvMC->decoderOn = 0; pViaXvMC->xvMCPort = tmpComm->xvmc_port; for (i=0; irendSurf[i] = 0; } strncpy(pViaXvMC->busIdString,tmpComm->busIdString,9); pViaXvMC->busIdString[9] = '\0'; pViaXvMC->attrib = tmpComm->initAttrs; setupAttribDesc(display, port, &pViaXvMC->attrib, pViaXvMC->attribDesc); /* * Must free the private data we were passed from X */ XFree(priv_data); /* * Map the register memory */ if(drmMap(pViaXvMC->fd,pViaXvMC->mmioOffset, pViaXvMC->mmioSize,&(pViaXvMC->mmioAddress)) < 0) { fprintf(stderr,"Unable to map the display chip mmio registers.\n"); _xvmc_destroy_context(display, context); free(pViaXvMC); return BadAlloc; } /* * Map Framebuffer memory */ if(drmMap(pViaXvMC->fd,pViaXvMC->fbOffset, pViaXvMC->fbSize,&(pViaXvMC->fbAddress)) < 0) { fprintf(stderr,"Unable to map XvMC Framebuffer.\n"); _xvmc_destroy_context(display, context); free(pViaXvMC); return BadAlloc; } /* * Map XvMC Sarea and get the address of the HW lock. */ if(drmMap(pViaXvMC->fd,pViaXvMC->sAreaOffset, pViaXvMC->sAreaSize,&(pViaXvMC->sAreaAddress)) < 0) { fprintf(stderr,"Unable to map DRI SAREA.\n"); _xvmc_destroy_context(display, context); free(pViaXvMC); return BadAlloc; } pViaXvMC->hwLock = (drmLockPtr) pViaXvMC->sAreaAddress; #ifndef BEAM_HWMPEG defaultQMatrices(pViaXvMC); pViaXvMC->yStride = (width + 31) & ~31; pViaXvMC->haveDecoder = 0; pViaXvMC->decTimeOut = 0; pViaXvMC->attribChanged = 1; HW_LOCK(pViaXvMC); viaVideoSubPictureOffLocked(pViaXvMC); HW_UNLOCK(pViaXvMC); #else printf("BEAM: XvMCCreateContext: Regs: %x %x\n", pViaXvMC->mmioOffset, pViaXvMC->mmioSize); pViaXvMC->yStride = (width + 31) & ~31; pViaXvMC->haveDecoder = 0; pViaXvMC->decTimeOut = 0; pViaXvMC->attribChanged = 1; HW_LOCK(pViaXvMC); hwMpegInit(&pViaXvMC->hwMpeg, pViaXvMC->fd, 0); viaVideoSubPictureOffLocked(pViaXvMC); HW_UNLOCK(pViaXvMC); #endif viaOpenRTC(pViaXvMC); return Success; } 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. */ #ifndef BEAM_HWMPEG hwMpegClose(pViaXvMC->hwMpeg); #endif releaseAttribDesc(pViaXvMC->attrib.numAttr,pViaXvMC->attribDesc); releaseDecoder(pViaXvMC,1); viaCloseRTC(pViaXvMC); drmUnmap(pViaXvMC->sAreaAddress,pViaXvMC->sAreaSize); drmUnmap(pViaXvMC->fbAddress,pViaXvMC->fbSize); drmUnmap(pViaXvMC->mmioAddress,pViaXvMC->mmioSize); _xvmc_destroy_context(display, context); free(pViaXvMC); context->privData = NULL; return Success; } 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; if(pViaXvMC == NULL) { return (error_base + XvMCBadContext); } surface->privData = (ViaXvMCSurface *)malloc(sizeof(ViaXvMCSurface)); if(!surface->privData) { return BadAlloc; } pViaSurface = (ViaXvMCSurface *)surface->privData; if((ret = _xvmc_create_surface(display, context, surface, &priv_count, &priv_data))) { free(pViaSurface); fprintf(stderr,"Unable to create XvMC Surface.\n"); return ret; } 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->port = context->port; pViaSurface->haveXv = 0; pViaSurface->width = context->width; pViaSurface->height = context->height; pViaSurface->yStride = pViaXvMC->yStride; pViaSurface->privContext = pViaXvMC; pViaSurface->privSubPic = NULL; return Success; } 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; _xvmc_destroy_surface(display,surface); if (pViaSurface->haveXv) { XFree(pViaSurface->xvImage); } free(pViaSurface); surface->privData = NULL; return Success; } 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); } if (!pViaXvMC->haveDecoder) { fprintf(stderr,"XvMCPutSlice: This context does not own decoder!\n"); return BadAlloc; } if (pViaXvMC->decTimeOut) return BadValue; HW_LOCK(pViaXvMC); #ifndef BEAM_HWMPEG if (decoderWaitLocked(pViaXvMC,1) & 0x70) { HW_UNLOCK(pViaXvMC); return BadValue; } viaMpegWriteSliceLocked(pViaXvMC, (CARD8 *)slice, nBytes, sCode); #else hwMpegWriteSlice(pViaXvMC->hwMpeg, (CARD8 *)slice, nBytes, sCode); #endif HW_UNLOCK(pViaXvMC); return Success; } 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); } if (!pViaXvMC->haveDecoder) { fprintf(stderr,"XvMCPutSlice: This context does not own decoder!\n"); return BadAlloc; } if (pViaXvMC->decTimeOut) return Success; HW_LOCK(pViaXvMC); #ifndef BEAM_HWMPEG if (decoderWaitLocked(pViaXvMC,1) & 0x70) { /* * The HW decoder detected an error */ HW_UNLOCK(pViaXvMC); return BadValue; } viaMpegWriteSliceLocked(pViaXvMC, (CARD8 *)slice, nBytes, 0); #else hwMpegWriteSlice(pViaXvMC->hwMpeg, (CARD8 *)slice, nBytes, 0); #endif HW_UNLOCK(pViaXvMC); return Success; } 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) { 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); } if (!pViaSurface->haveXv) { pViaSurface->xvImage = XvCreateImage(display,pViaSurface->port,FOURCC_VIA, (char *)&buf,pViaSurface->width, pViaSurface->height); pViaSurface->gc = XCreateGC(display,draw,0,0); pViaSurface->haveXv = 1; } pViaSurface->draw = draw; pViaSurface->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; if ((ret = XvPutImage(display,pViaSurface->port,draw,pViaSurface->gc, pViaSurface->xvImage,srcx,srcy,srcw,srch, destx,desty,destw,desth))) { fprintf(stderr,"XvMCPutSurface: Updating overlay failed.\n"); return ret; } pViaXvMC->attribChanged = 0; /* * Take care of subpicture displaying! */ sAPriv = SAREAPTR( pViaXvMC ); HW_LOCK(pViaXvMC); if (NULL != pViaSubPic) { if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] != (pViaSubPic->srfNo | VIA_XVMC_VALID)) { sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] = pViaSubPic->srfNo | VIA_XVMC_VALID; viaVideoSubPictureLocked(pViaSubPic); } } else { if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] & VIA_XVMC_VALID) { viaVideoSubPictureOffLocked(pViaXvMC); sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] &= ~VIA_XVMC_VALID; } } /* * Flip the surface into the overlay. */ sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort] = pViaSurface->srfNo | VIA_XVMC_VALID; viaVideoSWFlipLocked(pViaXvMC, flags, pViaSurface->progressiveSequence, yOffs(pViaSurface), uOffs(pViaSurface), vOffs(pViaSurface)); HW_UNLOCK(pViaXvMC); return Success; } 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); } 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; if((display == NULL) || (context == NULL) || (target_surface == NULL)) { return BadValue; } pViaXvMC = context->privData; /* * Grab decoder */ if (grabDecoder(pViaXvMC, &hadDecoderLast)) { return BadAlloc; } if (!hadDecoderLast) { pViaXvMC->intraLoaded = 0; pViaXvMC->nonIntraLoaded = 0; } pViaXvMC->haveDecoder = 1; 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; pViaXvMC->rendSurf[1] = futS->srfNo | VIA_XVMC_VALID; } if (past_surface) { pastS = (ViaXvMCSurface *)past_surface->privData; pViaXvMC->rendSurf[2] = pastS->srfNo | VIA_XVMC_VALID; } targS->progressiveSequence = (control->flags & XVMC_PROGRESSIVE_SEQUENCE); HW_LOCK(pViaXvMC); #ifndef BEAM_HWMPEG viaMpegResetLocked(pViaXvMC); pViaXvMC->decoderOn = 1; decoderWaitLocked(pViaXvMC,1); viaMpegSetFBLocked(pViaXvMC,0,yOffs(targS),uOffs(targS),vOffs(targS)); viaMpegSetSurfaceStrideLocked(pViaXvMC); if (past_surface) { viaMpegSetFBLocked(pViaXvMC,1,yOffs(pastS),uOffs(pastS),vOffs(pastS)); } else { viaMpegSetFBLocked(pViaXvMC,1,0xffffffff,0xffffffff,0xffffffff); } if (future_surface) { viaMpegSetFBLocked(pViaXvMC,2,yOffs(futS),uOffs(futS),vOffs(futS)); } else { viaMpegSetFBLocked(pViaXvMC,2,0xffffffff,0xffffffff,0xffffffff); } viaMpegBeginPictureLocked(pViaXvMC,context->width,context->height,control); #else { HwMpegConfig config; pViaXvMC->decoderOn = 1; /* TODO !!! */ config.width = context->width; config.height = context->height; config.targetFrame.data = targS->offsets[0]; if(future_surface) config.pastFrame.data = pastS->offsets[0]; else config.pastFrame.data = 0; if(past_surface) config.futureFrame.data = futS->offsets[0]; else config.futureFrame.data = 0; /* Should convert these */ config.flags = control->flags; config.mpeg_coding = control->mpeg_coding; config.picture_structure = control->picture_structure; config.picture_coding_type = control->picture_coding_type; config.intra_dc_precision = control->intra_dc_precision; config.FHMV_range = control->FHMV_range; config.FVMV_range = control->FVMV_range; config.BHMV_range = control->BHMV_range; config.BVMV_range = control->BVMV_range; hwMpegFieldStart(pViaXvMC->hwMpeg, &config); } #endif HW_UNLOCK(pViaXvMC); pViaXvMC->decTimeOut = 0; return Success; } Status XvMCSyncSurface(Display *display,XvMCSurface *surface) { ViaXvMCSurface *pViaSurface; ViaXvMCContext *pViaXvMC; volatile ViaXvMCSAreaPriv *sPriv; unsigned i; int retVal; 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); } if (!pViaXvMC->haveDecoder) { return Success; } retVal = Success; if (pViaXvMC->rendSurf[0] == (pViaSurface->srfNo | VIA_XVMC_VALID)) { sPriv = SAREAPTR(pViaXvMC); HW_LOCK(pViaXvMC); #ifndef BEAM_HWMPEG if (!pViaXvMC->decTimeOut) { if (decoderWaitLocked(pViaXvMC,0) & 0x70) { retVal = BadValue; } } else { viaMpegResetLocked(pViaXvMC); pViaXvMC->decTimeOut = 0; retVal = BadValue; } pViaXvMC->haveDecoder = 0; releaseDecoder(pViaXvMC, 0); #else if(hwMpegWaitComplete(pViaXvMC->hwMpeg)){ retVal = BadValue; } pViaXvMC->haveDecoder = 0; releaseDecoder(pViaXvMC, 0); #endif HW_UNLOCK(pViaXvMC); for (i=0; irendSurf[i] = 0; } } return retVal; } 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); } #ifndef BEAM_HWMPEG 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; } #else { HwMpegQMatrix qmatrix; if(qmx->load_intra_quantiser_matrix) { memcpy(qmatrix.intra_quantiser_matrix, qmx->intra_quantiser_matrix, sizeof(qmatrix.intra_quantiser_matrix)); pViaXvMC->intraLoaded = 0; } if (qmx->load_non_intra_quantiser_matrix) { memcpy(qmatrix.non_intra_quantiser_matrix, qmx->non_intra_quantiser_matrix, sizeof(qmatrix.non_intra_quantiser_matrix)); pViaXvMC->nonIntraLoaded = 0; } hwMpegLoadQMatrix(pViaXvMC->hwMpeg, &qmatrix); } #endif return Success; } /* * Below, we provide functions unusable for this implementation, but for * standard completeness. */ 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); } Status XvMCCreateBlocks ( Display *display, XvMCContext *context, unsigned int num_blocks, XvMCBlockArray * block ) { return (error_base + XvMCBadContext); } Status XvMCDestroyBlocks (Display *display, XvMCBlockArray * block) { return Success; } Status XvMCCreateMacroBlocks ( Display *display, XvMCContext *context, unsigned int num_blocks, XvMCMacroBlockArray * blocks ) { return (error_base + XvMCBadContext); } Status XvMCDestroyMacroBlocks(Display *display, XvMCMacroBlockArray * block) { return (error_base + XvMCBadContext); } 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; } pViaSubPic = (ViaXvMCSubPicture *)subpicture->privData; subpicture->width = context->width; subpicture->height = context->height; subpicture->xvimage_id = xvimage_id; subpicture->num_palette_entries = VIA_SUBPIC_PALETTE_SIZE; subpicture->entry_bytes = 3; strncpy(subpicture->component_order,"YUV",4); if((ret = _xvmc_create_subpicture(display, context, subpicture, &priv_count, &priv_data))) { free(pViaSubPic); fprintf(stderr,"Unable to create XvMC Subpicture.\n"); return ret; } 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); /* Free data returned from _xvmc_create_subpicture */ XFree(priv_data); return Success; } 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; sAPriv = SAREAPTR( pViaXvMC ); HW_LOCK(pViaXvMC); /* * If the subpicture is displaying, Immeadiately update it with the * new palette. */ if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] == (pViaSubPic->srfNo | VIA_XVMC_VALID)) { viaVideoSubPictureLocked(pViaSubPic); } HW_UNLOCK(pViaXvMC); 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; } 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; /* Clip clearing area so that it fits inside subpicture. */ if (findOverlap(subpicture->width, subpicture->height, &x, &y, &dummyX, &dummyY, &width, &height)) return Success; bOffs = pViaSubPic->offset + y*pViaSubPic->stride + x; viaBlit(pViaXvMC, 8, 0, pViaSubPic->stride, bOffs, pViaSubPic->stride, width, height, 1, 1, VIABLIT_FILL, color); return Success; } 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; /* * Clip copy area so that it fits inside subpicture and image. */ if (findOverlap(subpicture->width, subpicture->height, &dstx, &dsty, &srcx, &srcy, &width, &height)) return Success; if (findOverlap(image->width, image->height, &srcx, &srcy, &dstx, &dsty, &width, &height)) return Success; /* * We can't use hardware blitting here. Possibly drm dma blit when that * becomes available. */ HW_LOCK(pViaXvMC); for(i=0; ifbAddress) + (pViaSubPic->offset + (dsty+i)*pViaSubPic->stride + dstx)); sAddr = (((CARD8 *)image->data) + (image->offsets[0] + (srcy+i)*image->pitches[0] + srcx)); memcpy(dAddr,sAddr,width); } HW_UNLOCK(pViaXvMC); return Success; } 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; } 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; } viaBlit(pViaXvMC, 8, yOffs(pViaSSurface), pViaSSurface->yStride, yOffs(pViaSurface), pViaSurface->yStride, width, height, 1, 1, VIABLIT_COPY, 0); viaBlit(pViaXvMC, 8, uOffs(pViaSSurface), pViaSSurface->yStride >> 1, uOffs(pViaSurface), pViaSurface->yStride >> 1, width >> 1, height >> 1, 1, 1, VIABLIT_COPY, 0); viaBlit(pViaXvMC, 8, vOffs(pViaSSurface), pViaSSurface->yStride >> 1, vOffs(pViaSurface), pViaSurface->yStride >> 1, width >> 1, height >> 1, 1, 1, VIABLIT_COPY, 0); if (subpicture) { if(NULL == (pViaSubPic = subpicture->privData)) { return (error_base + XvMCBadSubpicture); } pViaSurface->privSubPic = pViaSubPic; } else { pViaSurface->privSubPic = NULL; } return Success; } Status XvMCSyncSubpicture (Display *display, XvMCSubpicture *subpicture) { ViaXvMCSubPicture *pViaSubPic; ViaXvMCContext *pViaXvMC; if((display == NULL) || subpicture == NULL){ return BadValue; } if(NULL == (pViaSubPic = subpicture->privData)) { return (error_base + XvMCBadSubpicture); } pViaXvMC = pViaSubPic->privContext; return Success; } Status XvMCFlushSubpicture (Display *display, XvMCSubpicture *subpicture) { ViaXvMCSubPicture *pViaSubPic; ViaXvMCContext *pViaXvMC; if((display == NULL) || subpicture == NULL){ return BadValue; } if(NULL == (pViaSubPic = subpicture->privData)) { return (error_base + XvMCBadSubpicture); } pViaXvMC = pViaSubPic->privContext; return Success; } 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; sAPriv = SAREAPTR(pViaXvMC); HW_LOCK(pViaXvMC); if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] == ( pViaSubPic->srfNo | VIA_XVMC_VALID )) { viaVideoSubPictureOffLocked(pViaXvMC); sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] = 0; } HW_UNLOCK(pViaXvMC); _xvmc_destroy_subpicture(display,subpicture); free(pViaSubPic); subpicture->privData = NULL; return Success; } 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; } Status XvMCFlushSurface (Display *display, XvMCSurface *surface) { ViaXvMCSurface *pViaSurface; if((display == NULL) || surface == NULL){ return BadValue; } if(NULL == (pViaSurface = surface->privData)) { return (error_base + XvMCBadSurface); } return Success; } Status XvMCGetSurfaceStatus (Display *display, XvMCSurface *surface, int *stat) { ViaXvMCSurface *pViaSurface; ViaXvMCContext *pViaXvMC; volatile ViaXvMCSAreaPriv *sAPriv; unsigned i; if((display == NULL) || surface == NULL){ return BadValue; } if(NULL == (pViaSurface = surface->privData)) { return (error_base + XvMCBadSurface); } if (stat) { *stat = 0; pViaXvMC = pViaSurface->privContext; sAPriv = SAREAPTR( pViaXvMC ); if (sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort] == (pViaSurface->srfNo | VIA_XVMC_VALID)) *stat |= XVMC_DISPLAYING; for (i=0; irendSurf[i] == (pViaSurface->srfNo | VIA_XVMC_VALID)) { *stat |= XVMC_RENDERING; break; } } } return Success; } 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; } if (NULL != (ret = (XvAttribute *) malloc(siz = VIA_NUM_XVMC_ATTRIBUTES*sizeof(XvAttribute)))) { memcpy(ret,pViaXvMC->attribDesc,siz); *number = VIA_NUM_XVMC_ATTRIBUTES; } return ret; } Status XvMCSetAttribute ( 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); } 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) return BadValue; pViaXvMC->attrib.attributes[i].value = value; found = 1; pViaXvMC->attribChanged = 1; break; } } if (!found) return BadMatch; return Success; } 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); } 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; } } } if (!found) return BadMatch; return Success; } 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); } if (!pViaSurface->haveXv) return Success; sAPriv = SAREAPTR( pViaXvMC ); HW_LOCK(pViaXvMC); if (sAPriv->XvMCDisplaying[pViaXvMC->xvMCPort] != (pViaSurface->srfNo | VIA_XVMC_VALID)) { HW_UNLOCK(pViaXvMC); return Success; } if (NULL != (pViaSubPic = pViaSurface->privSubPic)) { if (sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] == (pViaSubPic->srfNo | VIA_XVMC_VALID)) { sAPriv->XvMCSubPicOn[pViaXvMC->xvMCPort] &= ~VIA_XVMC_VALID; viaVideoSubPictureOffLocked(pViaXvMC); } } HW_UNLOCK(pViaXvMC); buf.command = VIA_XVMC_COMMAND_UNDISPLAY; buf.ctxNo = pViaXvMC->ctxNo | VIA_XVMC_VALID; buf.srfNo = pViaSurface->srfNo | VIA_XVMC_VALID; pViaSurface->xvImage->data = (char *)&buf; if ((ret = XvPutImage(display,pViaSurface->port,pViaSurface->draw, pViaSurface->gc, pViaSurface->xvImage,0,0,1,1,0,0,1,1))) { fprintf(stderr,"XvMCPutSurface: Hiding overlay failed.\n"); return ret; } return Success; }