63215a6123
bernd@ and oga@. Tested by many in snaphots.
1651 lines
41 KiB
C
1651 lines
41 KiB
C
/*****************************************************************************
|
|
* VIA Unichrome XvMC extension client lib.
|
|
*
|
|
* Copyright (c) 2004 Thomas Hellström. All rights reserved.
|
|
* Copyright (c) 2003 Andreas Robinson. 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.
|
|
*/
|
|
|
|
/*
|
|
* Low-level functions that deal directly with the hardware. In the future,
|
|
* these functions might be implemented in a kernel module. Also, some of them
|
|
* would benefit from DMA.
|
|
*
|
|
* Authors:
|
|
* Andreas Robinson 2003. (Initial decoder interface functions).
|
|
* Thomas Hellstrom 2004, 2005 (Blitting functions, AGP and locking, Unichrome Pro Video AGP).
|
|
* Ivor Hewitt 2005 (Unichrome Pro modifications and merging).
|
|
*/
|
|
|
|
/* IH
|
|
* I've left the proReg or-ing in case we need/want to implement the V1/V3
|
|
* register toggle too, which also moves the register locations.
|
|
* The CN400 has dual mpeg decoders, not sure at the moment whether these
|
|
* are also operated through independent registers also.
|
|
*/
|
|
|
|
#undef VIDEO_DMA
|
|
#define HQV_USE_IRQ
|
|
#define UNICHROME_PRO
|
|
|
|
#include "viaXvMCPriv.h"
|
|
#include "viaLowLevel.h"
|
|
#include "driDrawable.h"
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <stdio.h>
|
|
|
|
typedef enum
|
|
{ ll_init, ll_agpBuf, ll_pciBuf, ll_timeStamp, ll_llBuf }
|
|
LLState;
|
|
|
|
typedef struct
|
|
{
|
|
drm_via_mem_t mem;
|
|
unsigned offset;
|
|
unsigned stride;
|
|
unsigned height;
|
|
} LowLevelBuffer;
|
|
|
|
struct _XvMCLowLevel;
|
|
|
|
typedef struct _ViaCommandBuffer
|
|
{
|
|
CARD32 *buf;
|
|
CARD32 waitFlags;
|
|
unsigned pos;
|
|
unsigned bufSize;
|
|
int mode;
|
|
int header_start;
|
|
int rindex;
|
|
void (*flushFunc) (struct _ViaCommandBuffer * cb,
|
|
struct _XvMCLowLevel * xl);
|
|
} ViaCommandBuffer;
|
|
|
|
typedef struct _XvMCLowLevel
|
|
{
|
|
ViaCommandBuffer agpBuf, pciBuf, *videoBuf;
|
|
int use_agp;
|
|
int fd;
|
|
drm_context_t *drmcontext;
|
|
drmLockPtr hwLock;
|
|
drmAddress mmioAddress;
|
|
drmAddress fbAddress;
|
|
unsigned fbStride;
|
|
unsigned fbDepth;
|
|
unsigned width;
|
|
unsigned height;
|
|
int performLocking;
|
|
unsigned errors;
|
|
drm_via_mem_t tsMem;
|
|
CARD32 tsOffset;
|
|
volatile CARD32 *tsP;
|
|
CARD32 curTimeStamp;
|
|
CARD32 lastReadTimeStamp;
|
|
int agpSync;
|
|
CARD32 agpSyncTimeStamp;
|
|
unsigned chipId;
|
|
|
|
/*
|
|
* Data for video-engine less display
|
|
*/
|
|
|
|
XvMCRegion sRegion;
|
|
XvMCRegion dRegion;
|
|
LowLevelBuffer scale;
|
|
LowLevelBuffer back;
|
|
Bool downScaling;
|
|
CARD32 downScaleW;
|
|
CARD32 downScaleH;
|
|
CARD32 upScaleW;
|
|
CARD32 upScaleH;
|
|
unsigned fetch;
|
|
unsigned line;
|
|
LLState state;
|
|
} XvMCLowLevel;
|
|
|
|
/*
|
|
* For Other architectures than i386 these might have to be modified for
|
|
* bigendian etc.
|
|
*/
|
|
|
|
#define MPEGIN(xl,reg) \
|
|
*((volatile CARD32 *)(((CARD8 *)(xl)->mmioAddress) + 0xc00 + (reg)))
|
|
|
|
#define VIDIN(ctx,reg) \
|
|
*((volatile CARD32 *)(((CARD8 *)(ctx)->mmioAddress) + 0x200 + (reg)))
|
|
|
|
#define REGIN(ctx,reg) \
|
|
*((volatile CARD32 *)(((CARD8 *)(ctx)->mmioAddress) + 0x0000 + (reg)))
|
|
|
|
#define HQV_CONTROL 0x1D0
|
|
#define HQV_SRC_OFFSET 0x1CC
|
|
#define HQV_SRC_STARTADDR_Y 0x1D4
|
|
#define HQV_SRC_STARTADDR_U 0x1D8
|
|
#define HQV_SRC_STARTADDR_V 0x1DC
|
|
#define HQV_MINIFY_DEBLOCK 0x1E8
|
|
|
|
#define REG_HQV1_INDEX 0x00001000
|
|
|
|
#define HQV_SW_FLIP 0x00000010
|
|
#define HQV_FLIP_STATUS 0x00000001
|
|
#define HQV_SUBPIC_FLIP 0x00008000
|
|
#define HQV_FLIP_ODD 0x00000020
|
|
#define HQV_DEINTERLACE 0x00010000
|
|
#define HQV_FIELD_2_FRAME 0x00020000
|
|
#define HQV_FRAME_2_FIELD 0x00040000
|
|
#define HQV_FIELD_UV 0x00100000
|
|
#define HQV_DEBLOCK_HOR 0x00008000
|
|
#define HQV_DEBLOCK_VER 0x80000000
|
|
#define HQV_YUV420 0xC0000000
|
|
#define HQV_YUV422 0x80000000
|
|
#define HQV_ENABLE 0x08000000
|
|
#define HQV_GEN_IRQ 0x00000080
|
|
|
|
#define HQV_SCALE_ENABLE 0x00000800
|
|
#define HQV_SCALE_DOWN 0x00001000
|
|
|
|
#define V_COMPOSE_MODE 0x98
|
|
#define V1_COMMAND_FIRE 0x80000000
|
|
#define V3_COMMAND_FIRE 0x40000000
|
|
|
|
/* SUBPICTURE Registers */
|
|
#define SUBP_CONTROL_STRIDE 0x1C0
|
|
#define SUBP_STARTADDR 0x1C4
|
|
#define RAM_TABLE_CONTROL 0x1C8
|
|
#define RAM_TABLE_READ 0x1CC
|
|
|
|
/* SUBP_CONTROL_STRIDE 0x3c0 */
|
|
#define SUBP_HQV_ENABLE 0x00010000
|
|
#define SUBP_IA44 0x00020000
|
|
#define SUBP_AI44 0x00000000
|
|
#define SUBP_STRIDE_MASK 0x00001fff
|
|
#define SUBP_CONTROL_MASK 0x00070000
|
|
|
|
/* RAM_TABLE_CONTROL 0x3c8 */
|
|
#define RAM_TABLE_RGB_ENABLE 0x00000007
|
|
|
|
#define VIA_REG_STATUS 0x400
|
|
#define VIA_REG_GEMODE 0x004
|
|
#define VIA_REG_SRCBASE 0x030
|
|
#define VIA_REG_DSTBASE 0x034
|
|
#define VIA_REG_PITCH 0x038
|
|
#define VIA_REG_SRCCOLORKEY 0x01C
|
|
#define VIA_REG_KEYCONTROL 0x02C
|
|
#define VIA_REG_SRCPOS 0x008
|
|
#define VIA_REG_DSTPOS 0x00C
|
|
#define VIA_REG_GECMD 0x000
|
|
#define VIA_REG_DIMENSION 0x010 /* width and height */
|
|
#define VIA_REG_FGCOLOR 0x018
|
|
|
|
#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */
|
|
#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */
|
|
#define VIA_2D_ENG_BUSY 0x00000002 /* 2D Engine is busy */
|
|
#define VIA_3D_ENG_BUSY 0x00000001 /* 3D Engine is busy */
|
|
#define VIA_GEM_8bpp 0x00000000
|
|
#define VIA_GEM_16bpp 0x00000100
|
|
#define VIA_GEM_32bpp 0x00000300
|
|
#define VIA_GEC_BLT 0x00000001
|
|
#define VIA_PITCH_ENABLE 0x80000000
|
|
#define VIA_GEC_INCX 0x00000000
|
|
#define VIA_GEC_DECY 0x00004000
|
|
#define VIA_GEC_INCY 0x00000000
|
|
#define VIA_GEC_DECX 0x00008000
|
|
#define VIA_GEC_FIXCOLOR_PAT 0x00002000
|
|
|
|
#define VIA_BLIT_CLEAR 0x00
|
|
#define VIA_BLIT_COPY 0xCC
|
|
#define VIA_BLIT_FILL 0xF0
|
|
#define VIA_BLIT_SET 0xFF
|
|
|
|
#define VIA_SYNCWAITTIMEOUT 50000 /* Might be a bit conservative */
|
|
#define VIA_DMAWAITTIMEOUT 150000
|
|
#define VIA_VIDWAITTIMEOUT 50000
|
|
#define VIA_XVMC_DECODERTIMEOUT 50000 /*(microseconds) */
|
|
|
|
#define VIA_AGP_HEADER5 0xFE040000
|
|
#define VIA_AGP_HEADER6 0xFE050000
|
|
|
|
typedef struct
|
|
{
|
|
CARD32 data;
|
|
Bool set;
|
|
} HQVRegister;
|
|
|
|
#define H1_ADDR(val) (((val) >> 2) | 0xF0000000)
|
|
#define WAITFLAGS(cb, flags) \
|
|
(cb)->waitFlags |= (flags)
|
|
#define BEGIN_RING_AGP(cb, xl, size) \
|
|
do { \
|
|
if ((cb)->pos > ((cb)->bufSize-(size))) { \
|
|
cb->flushFunc(cb, xl); \
|
|
} \
|
|
} while(0)
|
|
#define OUT_RING_AGP(cb, val) do{ \
|
|
(cb)->buf[(cb)->pos++] = (val); \
|
|
} while(0);
|
|
|
|
#define OUT_RING_QW_AGP(cb, val1, val2) \
|
|
do { \
|
|
(cb)->buf[(cb)->pos++] = (val1); \
|
|
(cb)->buf[(cb)->pos++] = (val2); \
|
|
} while (0)
|
|
|
|
#define BEGIN_HEADER5_AGP(cb, xl, index) \
|
|
do { \
|
|
BEGIN_RING_AGP(cb, xl, 8); \
|
|
(cb)->mode = VIA_AGP_HEADER5; \
|
|
(cb)->rindex = (index); \
|
|
(cb)->header_start = (cb)->pos; \
|
|
(cb)->pos += 4; \
|
|
} while (0)
|
|
|
|
#define BEGIN_HEADER6_AGP(cb, xl) \
|
|
do { \
|
|
BEGIN_RING_AGP(cb, xl, 8); \
|
|
(cb)->mode = VIA_AGP_HEADER6; \
|
|
(cb)->header_start = (cb)->pos; \
|
|
(cb)->pos += 4; \
|
|
} while (0)
|
|
|
|
#define BEGIN_HEADER5_DATA(cb, xl, size, index) \
|
|
do { \
|
|
if ((cb)->pos > ((cb)->bufSize - ((size) + 16))) { \
|
|
cb->flushFunc(cb, xl); \
|
|
BEGIN_HEADER5_AGP(cb, xl, index); \
|
|
} else if ((cb)->mode && (((cb)->mode != VIA_AGP_HEADER5) || \
|
|
((cb)->rindex != index))) { \
|
|
finish_header_agp(cb); \
|
|
BEGIN_HEADER5_AGP((cb), xl, (index)); \
|
|
} else if (cb->mode != VIA_AGP_HEADER5) { \
|
|
BEGIN_HEADER5_AGP((cb), xl, (index)); \
|
|
} \
|
|
}while(0)
|
|
|
|
#define BEGIN_HEADER6_DATA(cb, xl, size) \
|
|
do{ \
|
|
if ((cb)->pos > (cb->bufSize-(((size) << 1) + 16))) { \
|
|
cb->flushFunc(cb, xl); \
|
|
BEGIN_HEADER6_AGP(cb, xl); \
|
|
} else if ((cb)->mode && ((cb)->mode != VIA_AGP_HEADER6)) { \
|
|
finish_header_agp(cb); \
|
|
BEGIN_HEADER6_AGP(cb, xl); \
|
|
} \
|
|
else if ((cb->mode != VIA_AGP_HEADER6)) { \
|
|
BEGIN_HEADER6_AGP(cb, (xl)); \
|
|
} \
|
|
}while(0)
|
|
|
|
#define HQV_SHADOW_BASE 0x3CC
|
|
#define HQV_SHADOW_SIZE 13
|
|
|
|
#define SETHQVSHADOW(shadow, offset, value) \
|
|
do { \
|
|
HQVRegister *r = (shadow) + (((offset) - HQV_SHADOW_BASE) >> 2); \
|
|
r->data = (value); \
|
|
r->set = TRUE; \
|
|
} while(0)
|
|
|
|
#define GETHQVSHADOW(shadow, offset) ((shadow)[(offset - HQV_SHADOW_BASE) >> 2].data)
|
|
|
|
#define LL_HW_LOCK(xl) \
|
|
do { \
|
|
DRM_LOCK((xl)->fd,(xl)->hwLock,*(xl)->drmcontext,0); \
|
|
} while(0);
|
|
#define LL_HW_UNLOCK(xl) \
|
|
do { \
|
|
DRM_UNLOCK((xl)->fd,(xl)->hwLock,*(xl)->drmcontext); \
|
|
} while(0);
|
|
|
|
static HQVRegister hqvShadow[HQV_SHADOW_SIZE];
|
|
|
|
static void
|
|
initHQVShadow(HQVRegister * r)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < HQV_SHADOW_SIZE; ++i) {
|
|
r->data = 0;
|
|
r++->set = FALSE;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
setHQVHWDeinterlacing(HQVRegister * shadow, Bool on, Bool motionDetect,
|
|
CARD32 stride, CARD32 height)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3E4);
|
|
|
|
if (!on) {
|
|
tmp &= ~((1 << 0) | (1 << 12) | (1 << 27) | (1 << 31));
|
|
SETHQVSHADOW(shadow, 0x3E4, tmp);
|
|
return;
|
|
}
|
|
|
|
tmp = (1 << 31) |
|
|
(4 << 28) |
|
|
(1 << 27) |
|
|
(3 << 25) | (1 << 18) | (2 << 14) | (8 << 8) | (8 << 1) | (1 << 0);
|
|
|
|
if (motionDetect)
|
|
tmp |= (1 << 12);
|
|
|
|
SETHQVSHADOW(shadow, 0x3E4, tmp);
|
|
|
|
tmp = GETHQVSHADOW(shadow, 0x3DC);
|
|
tmp |= (stride * height * 1536) / 1024 & 0x7ff;
|
|
|
|
SETHQVSHADOW(shadow, 0x3DC, tmp);
|
|
|
|
tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
tmp |= (1 << 23);
|
|
|
|
SETHQVSHADOW(shadow, 0x3D0, tmp);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
setHQVDeblocking(HQVRegister * shadow, Bool on, Bool lowPass)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3DC);
|
|
|
|
if (!on) {
|
|
tmp &= ~(1 << 27);
|
|
SETHQVSHADOW(shadow, 0x3DC, tmp);
|
|
return;
|
|
}
|
|
|
|
tmp |= (8 << 16) | (1 << 27);
|
|
if (lowPass)
|
|
tmp |= (1 << 26);
|
|
SETHQVSHADOW(shadow, 0x3DC, tmp);
|
|
|
|
tmp = GETHQVSHADOW(shadow, 0x3D4);
|
|
tmp |= (6 << 27);
|
|
SETHQVSHADOW(shadow, 0x3D4, tmp);
|
|
|
|
tmp = GETHQVSHADOW(shadow, 0x3D8);
|
|
tmp |= (19 << 27);
|
|
SETHQVSHADOW(shadow, 0x3D8, tmp);
|
|
}
|
|
|
|
static void
|
|
setHQVStartAddress(HQVRegister * shadow, unsigned yOffs, unsigned uOffs,
|
|
unsigned stride, unsigned format)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3D4);
|
|
|
|
tmp |= yOffs & 0x03FFFFF0;
|
|
SETHQVSHADOW(shadow, 0x3D4, tmp);
|
|
tmp = GETHQVSHADOW(shadow, 0x3D8);
|
|
tmp |= uOffs & 0x03FFFFF0;
|
|
SETHQVSHADOW(shadow, 0x3D8, tmp);
|
|
tmp = GETHQVSHADOW(shadow, 0x3F8);
|
|
tmp |= (stride & 0x1FF8);
|
|
SETHQVSHADOW(shadow, 0x3F8, tmp);
|
|
tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
|
|
if (format == 0) {
|
|
/*
|
|
* NV12
|
|
*/
|
|
tmp |= (0x0C << 28);
|
|
} else if (format == 1) {
|
|
/*
|
|
* RGB16
|
|
*/
|
|
tmp |= (0x02 << 28);
|
|
} else if (format == 2) {
|
|
/*
|
|
* RGB32
|
|
*/
|
|
;
|
|
}
|
|
SETHQVSHADOW(shadow, 0x3D0, tmp);
|
|
}
|
|
|
|
#if 0
|
|
|
|
static void
|
|
setHQVColorSpaceConversion(HQVRegister * shadow, unsigned depth, Bool on)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3DC);
|
|
|
|
if (!on) {
|
|
tmp &= ~(1 << 28);
|
|
SETHQVSHADOW(shadow, 0x3DC, tmp);
|
|
return;
|
|
}
|
|
|
|
if (depth == 32)
|
|
tmp |= (1 << 29);
|
|
tmp |= (1 << 28);
|
|
tmp &= ~(1 << 15);
|
|
SETHQVSHADOW(shadow, 0x3DC, tmp);
|
|
}
|
|
|
|
static void
|
|
setHQVFetchLine(HQVRegister * shadow, unsigned fetch, unsigned lines)
|
|
{
|
|
SETHQVSHADOW(shadow, 0x3E0,
|
|
((lines - 1) & 0x7FF) | (((fetch - 1) & 0x1FFF) << 16));
|
|
}
|
|
|
|
static void
|
|
setHQVScale(HQVRegister * shadow, unsigned horizontal, unsigned vertical)
|
|
{
|
|
SETHQVSHADOW(shadow, 0x3E8,
|
|
(horizontal & 0xFFFF) | ((vertical & 0xFFFF) << 16));
|
|
}
|
|
|
|
static void
|
|
setHQVSingleDestination(HQVRegister * shadow, unsigned offset,
|
|
unsigned stride)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
|
|
tmp |= (1 << 6);
|
|
|
|
SETHQVSHADOW(shadow, 0x3D0, tmp);
|
|
SETHQVSHADOW(shadow, 0x3EC, offset & 0x03FFFFF8);
|
|
SETHQVSHADOW(shadow, 0x3F4, stride & 0x1FF8);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
setHQVDeinterlacing(HQVRegister * shadow, CARD32 frameType)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
|
|
if ((frameType & XVMC_FRAME_PICTURE) == XVMC_TOP_FIELD) {
|
|
tmp |= HQV_FIELD_UV |
|
|
HQV_DEINTERLACE | HQV_FIELD_2_FRAME | HQV_FRAME_2_FIELD;
|
|
} else if ((frameType & XVMC_FRAME_PICTURE) == XVMC_BOTTOM_FIELD) {
|
|
tmp |= HQV_FIELD_UV |
|
|
HQV_DEINTERLACE |
|
|
HQV_FIELD_2_FRAME | HQV_FRAME_2_FIELD | HQV_FLIP_ODD;
|
|
}
|
|
SETHQVSHADOW(shadow, 0x3D0, tmp);
|
|
}
|
|
|
|
static void
|
|
setHQVTripleBuffer(HQVRegister * shadow, Bool on)
|
|
{
|
|
CARD32 tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
|
|
if (on)
|
|
tmp |= (1 << 26);
|
|
else
|
|
tmp &= ~(1 << 26);
|
|
SETHQVSHADOW(shadow, 0x3D0, tmp);
|
|
}
|
|
|
|
static void
|
|
finish_header_agp(ViaCommandBuffer * cb)
|
|
{
|
|
int numDWords, i;
|
|
|
|
CARD32 *hb;
|
|
|
|
if (!cb->mode)
|
|
return;
|
|
numDWords = cb->pos - cb->header_start - 4;
|
|
hb = cb->buf + cb->header_start;
|
|
switch (cb->mode) {
|
|
case VIA_AGP_HEADER5:
|
|
hb[0] = VIA_AGP_HEADER5 | cb->rindex;
|
|
hb[1] = numDWords;
|
|
hb[2] = 0x00F50000; /* SW debug flag. (?) */
|
|
break;
|
|
default:
|
|
hb[0] = VIA_AGP_HEADER6;
|
|
hb[1] = numDWords >> 1;
|
|
hb[2] = 0x00F60000; /* SW debug flag. (?) */
|
|
break;
|
|
}
|
|
hb[3] = 0;
|
|
if (numDWords & 3) {
|
|
for (i = 0; i < (4 - (numDWords & 3)); ++i)
|
|
OUT_RING_AGP(cb, 0x00000000);
|
|
}
|
|
cb->mode = 0;
|
|
}
|
|
|
|
void
|
|
hwlLock(void *xlp, int videoLock)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
LL_HW_LOCK(xl);
|
|
}
|
|
|
|
void
|
|
hwlUnlock(void *xlp, int videoLock)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
LL_HW_UNLOCK(xl);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
setAGPSyncLowLevel(void *xlp, int val, CARD32 timeStamp)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
xl->agpSync = val;
|
|
xl->agpSyncTimeStamp = timeStamp;
|
|
}
|
|
|
|
CARD32
|
|
viaDMATimeStampLowLevel(void *xlp)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
if (xl->use_agp) {
|
|
viaBlit(xl, 32, xl->tsOffset, 1, xl->tsOffset, 1, 1, 1, 0, 0,
|
|
VIABLIT_FILL, xl->curTimeStamp);
|
|
return xl->curTimeStamp++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
viaDMAWaitTimeStamp(XvMCLowLevel * xl, CARD32 timeStamp, int doSleep)
|
|
{
|
|
struct timeval now, then;
|
|
struct timezone here;
|
|
struct timespec sleep, rem;
|
|
|
|
if (xl->use_agp && (xl->lastReadTimeStamp - timeStamp > (1 << 23))) {
|
|
sleep.tv_nsec = 1;
|
|
sleep.tv_sec = 0;
|
|
here.tz_minuteswest = 0;
|
|
here.tz_dsttime = 0;
|
|
gettimeofday(&then, &here);
|
|
|
|
while (((xl->lastReadTimeStamp = *xl->tsP) - timeStamp) > (1 << 23)) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
|
|
if (((xl->lastReadTimeStamp =
|
|
*xl->tsP) - timeStamp) > (1 << 23)) {
|
|
xl->errors |= LL_DMA_TIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
viaDMAInitTimeStamp(XvMCLowLevel * xl)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (xl->use_agp) {
|
|
xl->tsMem.context = *(xl->drmcontext);
|
|
xl->tsMem.size = 64;
|
|
xl->tsMem.type = VIA_MEM_VIDEO;
|
|
if ((ret = drmCommandWriteRead(xl->fd, DRM_VIA_ALLOCMEM,
|
|
&xl->tsMem, sizeof(xl->tsMem))) < 0)
|
|
return ret;
|
|
if (xl->tsMem.size != 64)
|
|
return -1;
|
|
xl->tsOffset = (xl->tsMem.offset + 31) & ~31;
|
|
xl->tsP = (CARD32 *) xl->fbAddress + (xl->tsOffset >> 2);
|
|
xl->curTimeStamp = 1;
|
|
*xl->tsP = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
viaDMACleanupTimeStamp(XvMCLowLevel * xl)
|
|
{
|
|
|
|
if (!(xl->tsMem.size) || !xl->use_agp)
|
|
return 0;
|
|
return drmCommandWrite(xl->fd, DRM_VIA_FREEMEM, &xl->tsMem,
|
|
sizeof(xl->tsMem));
|
|
}
|
|
|
|
static CARD32
|
|
viaMpegGetStatus(XvMCLowLevel * xl)
|
|
{
|
|
return MPEGIN(xl, 0x54);
|
|
}
|
|
|
|
static int
|
|
viaMpegIsBusy(XvMCLowLevel * xl, CARD32 mask, CARD32 idle)
|
|
{
|
|
CARD32 tmp = viaMpegGetStatus(xl);
|
|
|
|
/*
|
|
* Error detected.
|
|
* FIXME: Are errors really shown when error concealment is on?
|
|
*/
|
|
|
|
if (tmp & 0x70)
|
|
return 0;
|
|
|
|
return (tmp & mask) != idle;
|
|
}
|
|
|
|
static void
|
|
syncDMA(XvMCLowLevel * xl, unsigned int doSleep)
|
|
{
|
|
|
|
/*
|
|
* Ideally, we'd like to have an interrupt wait here, but, according to second hand
|
|
* information, the hardware does not support this, although earlier S3 chips do that.
|
|
* It is therefore not implemented into the DRM, and we'll do a user space wait here.
|
|
*/
|
|
|
|
struct timeval now, then;
|
|
struct timezone here;
|
|
struct timespec sleep, rem;
|
|
|
|
sleep.tv_nsec = 1;
|
|
sleep.tv_sec = 0;
|
|
here.tz_minuteswest = 0;
|
|
here.tz_dsttime = 0;
|
|
gettimeofday(&then, &here);
|
|
while (!(REGIN(xl, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY)) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
|
|
if (!(REGIN(xl, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY)) {
|
|
xl->errors |= LL_DMA_TIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
while (REGIN(xl, VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
|
|
if (REGIN(xl, VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY) {
|
|
xl->errors |= LL_DMA_TIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
}
|
|
|
|
#ifdef HQV_USE_IRQ
|
|
static void
|
|
syncVideo(XvMCLowLevel * xl, unsigned int doSleep)
|
|
{
|
|
int proReg = REG_HQV1_INDEX;
|
|
|
|
/*
|
|
* Wait for HQV completion using completion interrupt. Nothing strange here.
|
|
* Note that the interrupt handler clears the HQV_FLIP_STATUS bit, so we
|
|
* can't wait on that one.
|
|
*/
|
|
|
|
if ((VIDIN(xl, HQV_CONTROL | proReg) & (HQV_SW_FLIP | HQV_SUBPIC_FLIP))) {
|
|
drm_via_irqwait_t irqw;
|
|
|
|
irqw.request.irq = 1;
|
|
irqw.request.type = VIA_IRQ_ABSOLUTE;
|
|
if (drmCommandWriteRead(xl->fd, DRM_VIA_WAIT_IRQ, &irqw,
|
|
sizeof(irqw)) < 0)
|
|
xl->errors |= LL_VIDEO_TIMEDOUT;
|
|
}
|
|
}
|
|
#else
|
|
static void
|
|
syncVideo(XvMCLowLevel * xl, unsigned int doSleep)
|
|
{
|
|
/*
|
|
* Wait for HQV completion. Nothing strange here. We assume that the HQV
|
|
* Handles syncing to the V1 / V3 engines by itself. It should be safe to
|
|
* always wait for SUBPIC_FLIP completion although subpictures are not always
|
|
* used.
|
|
*/
|
|
|
|
struct timeval now, then;
|
|
struct timezone here;
|
|
struct timespec sleep, rem;
|
|
|
|
int proReg = REG_HQV1_INDEX;
|
|
|
|
sleep.tv_nsec = 1;
|
|
sleep.tv_sec = 0;
|
|
here.tz_minuteswest = 0;
|
|
here.tz_dsttime = 0;
|
|
gettimeofday(&then, &here);
|
|
while ((VIDIN(xl,
|
|
HQV_CONTROL | proReg) & (HQV_SW_FLIP | HQV_SUBPIC_FLIP))) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_SYNCWAITTIMEOUT) {
|
|
if ((VIDIN(xl,
|
|
HQV_CONTROL | proReg) & (HQV_SW_FLIP |
|
|
HQV_SUBPIC_FLIP))) {
|
|
xl->errors |= LL_VIDEO_TIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
syncAccel(XvMCLowLevel * xl, unsigned int mode, unsigned int doSleep)
|
|
{
|
|
struct timeval now, then;
|
|
struct timezone here;
|
|
struct timespec sleep, rem;
|
|
CARD32 mask = ((mode & LL_MODE_2D) ? VIA_2D_ENG_BUSY : 0) |
|
|
((mode & LL_MODE_3D) ? VIA_3D_ENG_BUSY : 0);
|
|
|
|
sleep.tv_nsec = 1;
|
|
sleep.tv_sec = 0;
|
|
here.tz_minuteswest = 0;
|
|
here.tz_dsttime = 0;
|
|
gettimeofday(&then, &here);
|
|
while (REGIN(xl, VIA_REG_STATUS) & mask) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_SYNCWAITTIMEOUT) {
|
|
if (REGIN(xl, VIA_REG_STATUS) & mask) {
|
|
xl->errors |= LL_ACCEL_TIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
}
|
|
|
|
static void
|
|
syncMpeg(XvMCLowLevel * xl, unsigned int mode, unsigned int doSleep)
|
|
{
|
|
/*
|
|
* Ideally, we'd like to have an interrupt wait here, but from information from VIA
|
|
* at least the MPEG completion interrupt is broken on the CLE266, which was
|
|
* discovered during validation of the chip.
|
|
*/
|
|
|
|
struct timeval now, then;
|
|
struct timezone here;
|
|
struct timespec sleep, rem;
|
|
CARD32 busyMask = 0;
|
|
CARD32 idleVal = 0;
|
|
CARD32 ret;
|
|
|
|
sleep.tv_nsec = 1;
|
|
sleep.tv_sec = 0;
|
|
here.tz_minuteswest = 0;
|
|
here.tz_dsttime = 0;
|
|
gettimeofday(&then, &here);
|
|
if (mode & LL_MODE_DECODER_SLICE) {
|
|
busyMask = VIA_SLICEBUSYMASK;
|
|
idleVal = VIA_SLICEIDLEVAL;
|
|
}
|
|
if (mode & LL_MODE_DECODER_IDLE) {
|
|
busyMask |= VIA_BUSYMASK;
|
|
idleVal = VIA_IDLEVAL;
|
|
}
|
|
while (viaMpegIsBusy(xl, busyMask, idleVal)) {
|
|
gettimeofday(&now, &here);
|
|
if (timeDiff(&now, &then) > VIA_XVMC_DECODERTIMEOUT) {
|
|
if (viaMpegIsBusy(xl, busyMask, idleVal)) {
|
|
xl->errors |= LL_DECODER_TIMEDOUT;
|
|
}
|
|
break;
|
|
}
|
|
if (doSleep)
|
|
nanosleep(&sleep, &rem);
|
|
}
|
|
|
|
ret = viaMpegGetStatus(xl);
|
|
if (ret & 0x70) {
|
|
xl->errors |= ((ret & 0x70) >> 3);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
pciFlush(ViaCommandBuffer * cb, XvMCLowLevel * xl)
|
|
{
|
|
int ret;
|
|
drm_via_cmdbuffer_t b;
|
|
unsigned mode = cb->waitFlags;
|
|
|
|
finish_header_agp(cb);
|
|
b.buf = (char *)cb->buf;
|
|
b.size = cb->pos * sizeof(CARD32);
|
|
if (xl->performLocking)
|
|
hwlLock(xl, 0);
|
|
if (((mode == LL_MODE_VIDEO) && (xl->videoBuf == &xl->agpBuf)) ||
|
|
((mode != LL_MODE_VIDEO) && (mode != 0)))
|
|
syncDMA(xl, 0);
|
|
if ((mode & LL_MODE_2D) || (mode & LL_MODE_3D)) {
|
|
syncAccel(xl, mode, 0);
|
|
}
|
|
if (mode & LL_MODE_VIDEO) {
|
|
syncVideo(xl, 1);
|
|
}
|
|
if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE)) {
|
|
syncMpeg(xl, mode, 0);
|
|
}
|
|
ret = drmCommandWrite(xl->fd, DRM_VIA_PCICMD, &b, sizeof(b));
|
|
if (xl->performLocking)
|
|
hwlUnlock(xl, 0);
|
|
if (ret) {
|
|
xl->errors |= LL_PCI_COMMAND_ERR;
|
|
}
|
|
cb->pos = 0;
|
|
cb->waitFlags = 0;
|
|
}
|
|
|
|
static void
|
|
agpFlush(ViaCommandBuffer * cb, XvMCLowLevel * xl)
|
|
{
|
|
drm_via_cmdbuffer_t b;
|
|
int ret;
|
|
int i;
|
|
|
|
finish_header_agp(cb);
|
|
if (xl->use_agp) {
|
|
b.buf = (char *)cb->buf;
|
|
b.size = cb->pos * sizeof(CARD32);
|
|
if (xl->agpSync) {
|
|
syncXvMCLowLevel(xl, LL_MODE_DECODER_IDLE, 1,
|
|
xl->agpSyncTimeStamp);
|
|
xl->agpSync = 0;
|
|
}
|
|
if (xl->performLocking)
|
|
hwlLock(xl, 0);
|
|
do {
|
|
ret = drmCommandWrite(xl->fd, DRM_VIA_CMDBUFFER, &b, sizeof(b));
|
|
} while (-EAGAIN == ret);
|
|
if (xl->performLocking)
|
|
hwlUnlock(xl, 0);
|
|
|
|
if (ret) {
|
|
xl->errors |= LL_AGP_COMMAND_ERR;
|
|
for (i = 0; i < cb->pos; i += 2) {
|
|
printf("0x%x, 0x%x\n", (unsigned)cb->buf[i],
|
|
(unsigned)cb->buf[i + 1]);
|
|
}
|
|
exit(-1);
|
|
} else {
|
|
cb->pos = 0;
|
|
}
|
|
cb->waitFlags &= LL_MODE_VIDEO; /* FIXME: Check this! */
|
|
} else {
|
|
unsigned mode = cb->waitFlags;
|
|
|
|
b.buf = (char *)cb->buf;
|
|
b.size = cb->pos * sizeof(CARD32);
|
|
if (xl->performLocking)
|
|
hwlLock(xl, 0);
|
|
if (((mode == LL_MODE_VIDEO) && (cb == &xl->agpBuf)) ||
|
|
((mode != LL_MODE_VIDEO) && (mode != 0)))
|
|
syncDMA(xl, 0);
|
|
if ((mode & LL_MODE_2D) || (mode & LL_MODE_3D))
|
|
syncAccel(xl, mode, 0);
|
|
if (mode & LL_MODE_VIDEO)
|
|
syncVideo(xl, 1);
|
|
if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE))
|
|
syncMpeg(xl, mode, 0);
|
|
ret = drmCommandWrite(xl->fd, DRM_VIA_PCICMD, &b, sizeof(b));
|
|
if (xl->performLocking)
|
|
hwlUnlock(xl, 0);
|
|
if (ret) {
|
|
xl->errors |= LL_PCI_COMMAND_ERR;
|
|
}
|
|
cb->pos = 0;
|
|
cb->waitFlags = 0;
|
|
}
|
|
}
|
|
|
|
#if 0 /* Needs debugging */
|
|
static void
|
|
uploadHQVDeinterlace(XvMCLowLevel * xl, unsigned offset, HQVRegister * shadow,
|
|
CARD32 cur_offset, CARD32 prev_offset, CARD32 stride,
|
|
Bool top_field_first, CARD32 height)
|
|
{
|
|
CARD32 tmp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, 9);
|
|
tmp = GETHQVSHADOW(shadow, 0x3F8);
|
|
tmp &= ~(3 << 30);
|
|
tmp |= (1 << 30);
|
|
OUT_RING_QW_AGP(cb, 0x3F8 + offset, tmp);
|
|
OUT_RING_QW_AGP(cb, 0x3D4 + offset, prev_offset +
|
|
((top_field_first) ? stride : 0));
|
|
OUT_RING_QW_AGP(cb, 0x3D8 + offset, prev_offset + stride * height);
|
|
tmp &= ~(3 << 30);
|
|
tmp |= (2 << 30);
|
|
OUT_RING_QW_AGP(cb, 0x3F8 + offset, tmp);
|
|
OUT_RING_QW_AGP(cb, 0x3D4 + offset, cur_offset +
|
|
((top_field_first) ? 0 : stride));
|
|
OUT_RING_QW_AGP(cb, 0x3D8 + offset, cur_offset + stride * height);
|
|
tmp |= (3 << 30);
|
|
OUT_RING_QW_AGP(cb, 0x3F8 + offset, tmp);
|
|
OUT_RING_QW_AGP(cb, 0x3D4 + offset, cur_offset +
|
|
((top_field_first) ? stride : 0));
|
|
OUT_RING_QW_AGP(cb, 0x3D8 + offset, cur_offset + stride * height);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
uploadHQVShadow(XvMCLowLevel * xl, unsigned offset, HQVRegister * shadow,
|
|
Bool flip)
|
|
{
|
|
int i;
|
|
CARD32 tmp;
|
|
ViaCommandBuffer *cb = xl->videoBuf;
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, HQV_SHADOW_SIZE);
|
|
WAITFLAGS(cb, LL_MODE_VIDEO);
|
|
|
|
if (shadow[0].set)
|
|
OUT_RING_QW_AGP(cb, 0x3CC + offset, 0);
|
|
|
|
for (i = 2; i < HQV_SHADOW_SIZE; ++i) {
|
|
if (shadow[i].set) {
|
|
OUT_RING_QW_AGP(cb, offset + HQV_SHADOW_BASE + (i << 2),
|
|
shadow[i].data);
|
|
shadow[i].set = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finally the control register for flip.
|
|
*/
|
|
|
|
if (flip) {
|
|
tmp = GETHQVSHADOW(shadow, 0x3D0);
|
|
OUT_RING_QW_AGP(cb, offset + HQV_CONTROL + 0x200,
|
|
HQV_ENABLE | HQV_GEN_IRQ | HQV_SUBPIC_FLIP | HQV_SW_FLIP | tmp);
|
|
}
|
|
shadow[0].set = FALSE;
|
|
shadow[1].set = FALSE;
|
|
}
|
|
|
|
unsigned
|
|
flushXvMCLowLevel(void *xlp)
|
|
{
|
|
unsigned errors;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
if (xl->pciBuf.pos)
|
|
pciFlush(&xl->pciBuf, xl);
|
|
if (xl->agpBuf.pos)
|
|
agpFlush(&xl->agpBuf, xl);
|
|
errors = xl->errors;
|
|
if (errors)
|
|
printf("Error 0x%x\n", errors);
|
|
xl->errors = 0;
|
|
return errors;
|
|
}
|
|
|
|
void
|
|
flushPCIXvMCLowLevel(void *xlp)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
if (xl->pciBuf.pos)
|
|
pciFlush(&xl->pciBuf, xl);
|
|
if ((!xl->use_agp && xl->agpBuf.pos))
|
|
agpFlush(&xl->agpBuf, xl);
|
|
}
|
|
|
|
void
|
|
viaMpegSetSurfaceStride(void *xlp, ViaXvMCContext * ctx)
|
|
{
|
|
CARD32 y_stride = ctx->yStride;
|
|
CARD32 uv_stride = y_stride >> 1;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, 1);
|
|
OUT_RING_QW_AGP(cb, 0xc50, (y_stride >> 3) | ((uv_stride >> 3) << 16));
|
|
WAITFLAGS(cb, LL_MODE_DECODER_IDLE);
|
|
}
|
|
|
|
void
|
|
viaVideoSetSWFLipLocked(void *xlp, unsigned yOffs, unsigned uOffs,
|
|
unsigned vOffs, unsigned yStride, unsigned uvStride)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
initHQVShadow(hqvShadow);
|
|
setHQVStartAddress(hqvShadow, yOffs, vOffs, yStride, 0);
|
|
if (xl->videoBuf == &xl->agpBuf)
|
|
syncDMA(xl, 1);
|
|
syncVideo(xl, 1);
|
|
uploadHQVShadow(xl, REG_HQV1_INDEX, hqvShadow, FALSE);
|
|
xl->videoBuf->flushFunc(xl->videoBuf, xl);
|
|
}
|
|
|
|
void
|
|
viaVideoSWFlipLocked(void *xlp, unsigned flags, Bool progressiveSequence)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
setHQVDeinterlacing(hqvShadow, flags);
|
|
setHQVDeblocking(hqvShadow,
|
|
((flags & XVMC_FRAME_PICTURE) == XVMC_FRAME_PICTURE), TRUE);
|
|
setHQVTripleBuffer(hqvShadow, TRUE);
|
|
if (xl->videoBuf == &xl->agpBuf)
|
|
syncDMA(xl, 1);
|
|
syncVideo(xl, 1);
|
|
uploadHQVShadow(xl, REG_HQV1_INDEX, hqvShadow, TRUE);
|
|
xl->videoBuf->flushFunc(xl->videoBuf, xl);
|
|
}
|
|
|
|
void
|
|
viaMpegSetFB(void *xlp, unsigned i,
|
|
unsigned yOffs, unsigned uOffs, unsigned vOffs)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
i *= (4 * 2);
|
|
BEGIN_HEADER6_DATA(cb, xl, 2);
|
|
OUT_RING_QW_AGP(cb, 0xc28 + i, yOffs >> 3);
|
|
OUT_RING_QW_AGP(cb, 0xc2c + i, vOffs >> 3);
|
|
|
|
WAITFLAGS(cb, LL_MODE_DECODER_IDLE);
|
|
}
|
|
|
|
void
|
|
viaMpegBeginPicture(void *xlp, ViaXvMCContext * ctx,
|
|
unsigned width, unsigned height, const XvMCMpegControl * control)
|
|
{
|
|
|
|
unsigned j, mb_width, mb_height;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
mb_width = (width + 15) >> 4;
|
|
|
|
mb_height =
|
|
((control->mpeg_coding == XVMC_MPEG_2) &&
|
|
(control->flags & XVMC_PROGRESSIVE_SEQUENCE)) ?
|
|
2 * ((height + 31) >> 5) : (((height + 15) >> 4));
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, 72);
|
|
WAITFLAGS(cb, LL_MODE_DECODER_IDLE);
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc00,
|
|
((control->picture_structure & XVMC_FRAME_PICTURE) << 2) |
|
|
((control->picture_coding_type & 3) << 4) |
|
|
((control->flags & XVMC_ALTERNATE_SCAN) ? (1 << 6) : 0));
|
|
|
|
if (!(ctx->intraLoaded)) {
|
|
OUT_RING_QW_AGP(cb, 0xc5c, 0);
|
|
for (j = 0; j < 64; j += 4) {
|
|
OUT_RING_QW_AGP(cb, 0xc60,
|
|
ctx->intra_quantiser_matrix[j] |
|
|
(ctx->intra_quantiser_matrix[j + 1] << 8) |
|
|
(ctx->intra_quantiser_matrix[j + 2] << 16) |
|
|
(ctx->intra_quantiser_matrix[j + 3] << 24));
|
|
}
|
|
ctx->intraLoaded = 1;
|
|
}
|
|
|
|
if (!(ctx->nonIntraLoaded)) {
|
|
OUT_RING_QW_AGP(cb, 0xc5c, 1);
|
|
for (j = 0; j < 64; j += 4) {
|
|
OUT_RING_QW_AGP(cb, 0xc60,
|
|
ctx->non_intra_quantiser_matrix[j] |
|
|
(ctx->non_intra_quantiser_matrix[j + 1] << 8) |
|
|
(ctx->non_intra_quantiser_matrix[j + 2] << 16) |
|
|
(ctx->non_intra_quantiser_matrix[j + 3] << 24));
|
|
}
|
|
ctx->nonIntraLoaded = 1;
|
|
}
|
|
|
|
if (!(ctx->chromaIntraLoaded)) {
|
|
OUT_RING_QW_AGP(cb, 0xc5c, 2);
|
|
for (j = 0; j < 64; j += 4) {
|
|
OUT_RING_QW_AGP(cb, 0xc60,
|
|
ctx->chroma_intra_quantiser_matrix[j] |
|
|
(ctx->chroma_intra_quantiser_matrix[j + 1] << 8) |
|
|
(ctx->chroma_intra_quantiser_matrix[j + 2] << 16) |
|
|
(ctx->chroma_intra_quantiser_matrix[j + 3] << 24));
|
|
}
|
|
ctx->chromaIntraLoaded = 1;
|
|
}
|
|
|
|
if (!(ctx->chromaNonIntraLoaded)) {
|
|
OUT_RING_QW_AGP(cb, 0xc5c, 3);
|
|
for (j = 0; j < 64; j += 4) {
|
|
OUT_RING_QW_AGP(cb, 0xc60,
|
|
ctx->chroma_non_intra_quantiser_matrix[j] |
|
|
(ctx->chroma_non_intra_quantiser_matrix[j + 1] << 8) |
|
|
(ctx->chroma_non_intra_quantiser_matrix[j + 2] << 16) |
|
|
(ctx->chroma_non_intra_quantiser_matrix[j + 3] << 24));
|
|
}
|
|
ctx->chromaNonIntraLoaded = 1;
|
|
}
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc90,
|
|
((mb_width * mb_height) & 0x3fff) |
|
|
((control->flags & XVMC_PRED_DCT_FRAME) ? (1 << 14) : 0) |
|
|
((control->flags & XVMC_TOP_FIELD_FIRST) ? (1 << 15) : 0) |
|
|
((control->mpeg_coding == XVMC_MPEG_2) ? (1 << 16) : 0) |
|
|
((mb_width & 0xff) << 18));
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc94,
|
|
((control->flags & XVMC_CONCEALMENT_MOTION_VECTORS) ? 1 : 0) |
|
|
((control->flags & XVMC_Q_SCALE_TYPE) ? 2 : 0) |
|
|
((control->intra_dc_precision & 3) << 2) |
|
|
(((1 + 0x100000 / mb_width) & 0xfffff) << 4) |
|
|
((control->flags & XVMC_INTRA_VLC_FORMAT) ? (1 << 24) : 0));
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc98,
|
|
(((control->FHMV_range) & 0xf) << 0) |
|
|
(((control->FVMV_range) & 0xf) << 4) |
|
|
(((control->BHMV_range) & 0xf) << 8) |
|
|
(((control->BVMV_range) & 0xf) << 12) |
|
|
((control->flags & XVMC_SECOND_FIELD) ? (1 << 20) : 0) |
|
|
(0x0a6 << 16));
|
|
|
|
}
|
|
|
|
void
|
|
viaMpegReset(void *xlp)
|
|
{
|
|
int i, j;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, 99);
|
|
WAITFLAGS(cb, LL_MODE_DECODER_IDLE);
|
|
|
|
OUT_RING_QW_AGP(cb, 0xcf0, 0);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
OUT_RING_QW_AGP(cb, 0xcc0, 0);
|
|
OUT_RING_QW_AGP(cb, 0xc0c, 0x43 | 0x20);
|
|
for (j = 0xc10; j < 0xc20; j += 4)
|
|
OUT_RING_QW_AGP(cb, j, 0);
|
|
}
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc0c, 0x1c3);
|
|
for (j = 0xc10; j < 0xc20; j += 4)
|
|
OUT_RING_QW_AGP(cb, j, 0);
|
|
|
|
for (i = 0; i < 19; i++)
|
|
OUT_RING_QW_AGP(cb, 0xc08, 0);
|
|
|
|
OUT_RING_QW_AGP(cb, 0xc98, 0x400000);
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
OUT_RING_QW_AGP(cb, 0xcc0, 0);
|
|
OUT_RING_QW_AGP(cb, 0xc0c, 0x1c3 | 0x20);
|
|
for (j = 0xc10; j < 0xc20; j += 4)
|
|
OUT_RING_QW_AGP(cb, j, 0);
|
|
}
|
|
OUT_RING_QW_AGP(cb, 0xcf0, 0);
|
|
|
|
}
|
|
|
|
void
|
|
viaMpegWriteSlice(void *xlp, CARD8 * slice, int nBytes, CARD32 sCode)
|
|
{
|
|
int i, n, r;
|
|
CARD32 *buf;
|
|
int count;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
if (xl->errors & (LL_DECODER_TIMEDOUT |
|
|
LL_IDCT_FIFO_ERROR | LL_SLICE_FIFO_ERROR | LL_SLICE_FAULT))
|
|
return;
|
|
|
|
n = nBytes >> 2;
|
|
if (sCode)
|
|
nBytes += 4;
|
|
r = nBytes & 3;
|
|
buf = (CARD32 *) slice;
|
|
|
|
if (r)
|
|
nBytes += 4 - r;
|
|
|
|
nBytes += 8;
|
|
|
|
BEGIN_HEADER6_DATA(cb, xl, 2);
|
|
WAITFLAGS(cb, LL_MODE_DECODER_IDLE);
|
|
OUT_RING_QW_AGP(cb, 0xc9c, nBytes);
|
|
|
|
if (sCode)
|
|
OUT_RING_QW_AGP(cb, 0xca0, sCode);
|
|
|
|
i = 0;
|
|
count = 0;
|
|
|
|
do {
|
|
count += (LL_AGP_CMDBUF_SIZE - 20);
|
|
count = (count > n) ? n : count;
|
|
BEGIN_HEADER5_DATA(cb, xl, (count - i), 0xca0);
|
|
|
|
for (; i < count; i++) {
|
|
OUT_RING_AGP(cb, *buf++);
|
|
}
|
|
finish_header_agp(cb);
|
|
} while (i < n);
|
|
|
|
BEGIN_HEADER5_DATA(cb, xl, 3, 0xca0);
|
|
|
|
if (r) {
|
|
OUT_RING_AGP(cb, *buf & ((1 << (r << 3)) - 1));
|
|
}
|
|
OUT_RING_AGP(cb, 0);
|
|
OUT_RING_AGP(cb, 0);
|
|
finish_header_agp(cb);
|
|
}
|
|
|
|
void
|
|
viaVideoSubPictureOffLocked(void *xlp)
|
|
{
|
|
|
|
CARD32 stride;
|
|
int proReg = REG_HQV1_INDEX;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = xl->videoBuf;
|
|
|
|
if (xl->videoBuf == &xl->agpBuf)
|
|
syncDMA(xl, 1);
|
|
stride = VIDIN(xl, proReg | SUBP_CONTROL_STRIDE);
|
|
WAITFLAGS(cb, LL_MODE_VIDEO);
|
|
BEGIN_HEADER6_DATA(cb, xl, 1);
|
|
OUT_RING_QW_AGP(cb, proReg | SUBP_CONTROL_STRIDE | 0x200,
|
|
stride & ~SUBP_HQV_ENABLE);
|
|
}
|
|
|
|
void
|
|
viaVideoSubPictureLocked(void *xlp, ViaXvMCSubPicture * pViaSubPic)
|
|
{
|
|
|
|
unsigned i;
|
|
CARD32 cWord;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
int proReg = REG_HQV1_INDEX;
|
|
ViaCommandBuffer *cb = xl->videoBuf;
|
|
|
|
if (xl->videoBuf == &xl->agpBuf)
|
|
syncDMA(xl, 1);
|
|
WAITFLAGS(cb, LL_MODE_VIDEO);
|
|
BEGIN_HEADER6_DATA(cb, xl, VIA_SUBPIC_PALETTE_SIZE + 2);
|
|
for (i = 0; i < VIA_SUBPIC_PALETTE_SIZE; ++i) {
|
|
OUT_RING_QW_AGP(cb, proReg | RAM_TABLE_CONTROL | 0x200,
|
|
pViaSubPic->palette[i]);
|
|
}
|
|
|
|
cWord = (pViaSubPic->stride & SUBP_STRIDE_MASK) | SUBP_HQV_ENABLE;
|
|
cWord |= (pViaSubPic->ia44) ? SUBP_IA44 : SUBP_AI44;
|
|
OUT_RING_QW_AGP(cb, proReg | SUBP_STARTADDR | 0x200, pViaSubPic->offset);
|
|
OUT_RING_QW_AGP(cb, proReg | SUBP_CONTROL_STRIDE | 0x200, cWord);
|
|
}
|
|
|
|
void
|
|
viaBlit(void *xlp, unsigned bpp, unsigned srcBase,
|
|
unsigned srcPitch, unsigned dstBase, unsigned dstPitch,
|
|
unsigned w, unsigned h, int xdir, int ydir, unsigned blitMode,
|
|
unsigned color)
|
|
{
|
|
|
|
CARD32 dwGEMode = 0, srcY = 0, srcX, dstY = 0, dstX;
|
|
CARD32 cmd;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
ViaCommandBuffer *cb = &xl->agpBuf;
|
|
|
|
if (!w || !h)
|
|
return;
|
|
|
|
finish_header_agp(cb);
|
|
|
|
switch (bpp) {
|
|
case 16:
|
|
dwGEMode |= VIA_GEM_16bpp;
|
|
break;
|
|
case 32:
|
|
dwGEMode |= VIA_GEM_32bpp;
|
|
break;
|
|
default:
|
|
dwGEMode |= VIA_GEM_8bpp;
|
|
break;
|
|
}
|
|
|
|
srcX = srcBase & 31;
|
|
dstX = dstBase & 31;
|
|
switch (bpp) {
|
|
case 16:
|
|
dwGEMode |= VIA_GEM_16bpp;
|
|
srcX >>= 2;
|
|
dstX >>= 2;
|
|
break;
|
|
case 32:
|
|
dwGEMode |= VIA_GEM_32bpp;
|
|
srcX >>= 4;
|
|
dstX >>= 4;
|
|
break;
|
|
default:
|
|
dwGEMode |= VIA_GEM_8bpp;
|
|
break;
|
|
}
|
|
|
|
BEGIN_RING_AGP(cb, xl, 20);
|
|
WAITFLAGS(cb, LL_MODE_2D);
|
|
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_GEMODE), dwGEMode);
|
|
cmd = 0;
|
|
|
|
if (xdir < 0) {
|
|
cmd |= VIA_GEC_DECX;
|
|
srcX += (w - 1);
|
|
dstX += (w - 1);
|
|
}
|
|
if (ydir < 0) {
|
|
cmd |= VIA_GEC_DECY;
|
|
srcY += (h - 1);
|
|
dstY += (h - 1);
|
|
}
|
|
|
|
switch (blitMode) {
|
|
case VIABLIT_TRANSCOPY:
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_SRCCOLORKEY), color);
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_KEYCONTROL), 0x4000);
|
|
cmd |= VIA_GEC_BLT | (VIA_BLIT_COPY << 24);
|
|
break;
|
|
case VIABLIT_FILL:
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_FGCOLOR), color);
|
|
cmd |= VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT | (VIA_BLIT_FILL << 24);
|
|
break;
|
|
default:
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_KEYCONTROL), 0x0);
|
|
cmd |= VIA_GEC_BLT | (VIA_BLIT_COPY << 24);
|
|
}
|
|
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_SRCBASE), (srcBase & ~31) >> 3);
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_DSTBASE), (dstBase & ~31) >> 3);
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_PITCH), VIA_PITCH_ENABLE |
|
|
(srcPitch >> 3) | (((dstPitch) >> 3) << 16));
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_SRCPOS), ((srcY << 16) | srcX));
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_DSTPOS), ((dstY << 16) | dstX));
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_DIMENSION),
|
|
(((h - 1) << 16) | (w - 1)));
|
|
OUT_RING_QW_AGP(cb, H1_ADDR(VIA_REG_GECMD), cmd);
|
|
}
|
|
|
|
unsigned
|
|
syncXvMCLowLevel(void *xlp, unsigned int mode, unsigned int doSleep,
|
|
CARD32 timeStamp)
|
|
{
|
|
unsigned errors;
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
if (mode == 0) {
|
|
errors = xl->errors;
|
|
xl->errors = 0;
|
|
return errors;
|
|
}
|
|
|
|
if ((mode & (LL_MODE_VIDEO | LL_MODE_3D)) || !xl->use_agp) {
|
|
if (xl->performLocking)
|
|
hwlLock(xl, 0);
|
|
if ((xl->videoBuf == &xl->agpBuf) || (mode != LL_MODE_VIDEO))
|
|
syncDMA(xl, doSleep);
|
|
if (mode & LL_MODE_3D)
|
|
syncAccel(xl, mode, doSleep);
|
|
if (mode & LL_MODE_VIDEO)
|
|
syncVideo(xl, doSleep);
|
|
if (xl->performLocking)
|
|
hwlUnlock(xl, 0);
|
|
} else {
|
|
viaDMAWaitTimeStamp(xl, timeStamp, doSleep);
|
|
}
|
|
|
|
if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE))
|
|
syncMpeg(xl, mode, doSleep);
|
|
|
|
errors = xl->errors;
|
|
xl->errors = 0;
|
|
|
|
return errors;
|
|
}
|
|
|
|
static int
|
|
updateLowLevelBuf(XvMCLowLevel * xl, LowLevelBuffer * buf,
|
|
unsigned width, unsigned height)
|
|
{
|
|
unsigned stride, size;
|
|
drm_via_mem_t *mem = &buf->mem;
|
|
int ret;
|
|
|
|
stride = (width + 31) & ~31;
|
|
size = stride * height + (xl->fbDepth >> 3);
|
|
|
|
if (size != mem->size) {
|
|
if (mem->size)
|
|
drmCommandWrite(xl->fd, DRM_VIA_FREEMEM, mem, sizeof(mem));
|
|
mem->context = *(xl->drmcontext);
|
|
mem->size = size;
|
|
mem->type = VIA_MEM_VIDEO;
|
|
|
|
if (((ret = drmCommandWriteRead(xl->fd, DRM_VIA_ALLOCMEM, mem,
|
|
sizeof(mem))) < 0) || mem->size != size) {
|
|
mem->size = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
buf->offset = (mem->offset + 31) & ~31;
|
|
buf->stride = stride;
|
|
buf->height = height;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
cleanupLowLevelBuf(XvMCLowLevel * xl, LowLevelBuffer * buf)
|
|
{
|
|
drm_via_mem_t *mem = &buf->mem;
|
|
|
|
if (mem->size)
|
|
drmCommandWrite(xl->fd, DRM_VIA_FREEMEM, mem, sizeof(mem));
|
|
mem->size = 0;
|
|
}
|
|
|
|
static void *
|
|
releaseXvMCLowLevel(XvMCLowLevel * xl)
|
|
{
|
|
switch (xl->state) {
|
|
case ll_llBuf:
|
|
cleanupLowLevelBuf(xl, &xl->scale);
|
|
case ll_timeStamp:
|
|
viaDMACleanupTimeStamp(xl);
|
|
case ll_pciBuf:
|
|
free(xl->pciBuf.buf);
|
|
case ll_agpBuf:
|
|
free(xl->agpBuf.buf);
|
|
case ll_init:
|
|
free(xl);
|
|
default:
|
|
;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *
|
|
initXvMCLowLevel(int fd, drm_context_t * ctx,
|
|
drmLockPtr hwLock, drmAddress mmioAddress,
|
|
drmAddress fbAddress, unsigned fbStride, unsigned fbDepth,
|
|
unsigned width, unsigned height, int useAgp, unsigned chipId)
|
|
{
|
|
XvMCLowLevel *xl;
|
|
|
|
if (chipId != PCI_CHIP_VT3259 && chipId != PCI_CHIP_VT3364) {
|
|
fprintf(stderr, "You are using an XvMC driver for the wrong chip.\n");
|
|
fprintf(stderr, "Chipid is 0x%04x.\n", chipId);
|
|
return NULL;
|
|
}
|
|
|
|
xl = (XvMCLowLevel *) malloc(sizeof(XvMCLowLevel));
|
|
if (!xl)
|
|
return NULL;
|
|
xl->state = ll_init;
|
|
|
|
xl->agpBuf.buf = (CARD32 *) malloc(LL_AGP_CMDBUF_SIZE * sizeof(CARD32));
|
|
if (!xl->agpBuf.buf)
|
|
return releaseXvMCLowLevel(xl);
|
|
xl->state = ll_agpBuf;
|
|
xl->agpBuf.bufSize = LL_AGP_CMDBUF_SIZE;
|
|
xl->agpBuf.flushFunc = &agpFlush;
|
|
xl->agpBuf.pos = 0;
|
|
xl->agpBuf.mode = 0;
|
|
xl->agpBuf.waitFlags = 0;
|
|
|
|
xl->pciBuf.buf = (CARD32 *) malloc(LL_PCI_CMDBUF_SIZE * sizeof(CARD32));
|
|
if (!xl->pciBuf.buf)
|
|
return releaseXvMCLowLevel(xl);
|
|
xl->state = ll_pciBuf;
|
|
xl->pciBuf.bufSize = LL_PCI_CMDBUF_SIZE;
|
|
xl->pciBuf.flushFunc = &pciFlush;
|
|
xl->pciBuf.pos = 0;
|
|
xl->pciBuf.mode = 0;
|
|
xl->pciBuf.waitFlags = 0;
|
|
|
|
xl->use_agp = useAgp;
|
|
xl->fd = fd;
|
|
xl->drmcontext = ctx;
|
|
xl->hwLock = hwLock;
|
|
xl->mmioAddress = mmioAddress;
|
|
xl->fbAddress = fbAddress;
|
|
xl->fbDepth = fbDepth;
|
|
xl->fbStride = fbStride;
|
|
xl->width = width;
|
|
xl->height = height;
|
|
xl->performLocking = 1;
|
|
xl->errors = 0;
|
|
xl->agpSync = 0;
|
|
xl->chipId = chipId;
|
|
|
|
if (viaDMAInitTimeStamp(xl))
|
|
return releaseXvMCLowLevel(xl);
|
|
xl->state = ll_timeStamp;
|
|
|
|
xl->scale.mem.size = 0;
|
|
xl->back.mem.size = 0;
|
|
|
|
if (updateLowLevelBuf(xl, &xl->scale, width, height))
|
|
return releaseXvMCLowLevel(xl);
|
|
xl->state = ll_llBuf;
|
|
|
|
#ifdef VIDEO_DMA
|
|
xl->videoBuf = &xl->agpBuf;
|
|
#else
|
|
xl->videoBuf = &xl->pciBuf;
|
|
#endif
|
|
|
|
return xl;
|
|
}
|
|
|
|
void
|
|
setLowLevelLocking(void *xlp, int performLocking)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
xl->performLocking = performLocking;
|
|
}
|
|
|
|
void
|
|
closeXvMCLowLevel(void *xlp)
|
|
{
|
|
XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
|
|
|
|
releaseXvMCLowLevel(xl);
|
|
}
|
|
|
|
#if 0 /* Under development */
|
|
static CARD32
|
|
computeDownScaling(int dst, int *src)
|
|
{
|
|
CARD32 value = 0x800;
|
|
|
|
while (*src > dst) {
|
|
*src >>= 1;
|
|
value--;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static void
|
|
computeHQVScaleAndFilter(XvMCLowLevel * xl)
|
|
{
|
|
int srcW, srcH;
|
|
const XvMCRegion *src = &xl->sRegion, *back = &xl->dRegion;
|
|
|
|
xl->downScaling = FALSE;
|
|
|
|
if (back->w < src->w || back->h < src->h) {
|
|
|
|
xl->downScaling = TRUE;
|
|
srcW = src->w;
|
|
srcH = src->h;
|
|
|
|
xl->downScaleW = (back->w >= srcW) ? 0 :
|
|
HQV_SCALE_ENABLE | HQV_SCALE_DOWN |
|
|
(computeDownScaling(back->w, &srcW));
|
|
|
|
xl->downScaleH = (back->h >= srcH) ? 0 :
|
|
HQV_SCALE_ENABLE | HQV_SCALE_DOWN |
|
|
(computeDownScaling(back->h, &srcH));
|
|
|
|
}
|
|
|
|
xl->upScaleW =
|
|
(back->w == srcW) ? 0 : (0x800 * srcW / back->w) | HQV_SCALE_ENABLE;
|
|
xl->upScaleH =
|
|
(back->h == srcH) ? 0 : (0x800 * srcH / back->h) | HQV_SCALE_ENABLE;
|
|
}
|
|
|
|
static int
|
|
setupBackBuffer(XvMCLowLevel * xl)
|
|
{
|
|
return updateLowLevelBuf(xl, &xl->back, xl->dRegion.w, xl->dRegion.h);
|
|
}
|
|
|
|
#endif
|