summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Hellstrom <unichrome@shipmail.org>2005-06-28 23:08:46 +0000
committerThomas Hellstrom <unichrome@shipmail.org>2005-06-28 23:08:46 +0000
commitb8456217647a289d648f026d993165196739d8ab (patch)
tree2c2d83ac29e089f264357bd394c55aade6c666e9
parent24f59186f2f5a187e603aa8762182f562f72be1c (diff)
Add XvMC support for the hw mpeg1 / mpeg2 decoder of VIA's Unichrome ProXORG-6_8_99_14
Group A chips, CN400 and PM8X0. (Ivor Hewitt and Thomas Hellstrom)
-rw-r--r--src/via_xvmc.c3
-rw-r--r--src/xvmc/unichromeProA/viaLowLevelPro.c1465
2 files changed, 1467 insertions, 1 deletions
diff --git a/src/via_xvmc.c b/src/via_xvmc.c
index e9c4c77..dd545a5 100644
--- a/src/via_xvmc.c
+++ b/src/via_xvmc.c
@@ -329,7 +329,8 @@ ViaInitXVMC(ScreenPtr pScreen)
pVia->XvMCEnabled = 0;
- if (!(pVia->Chipset == VIA_CLE266) && !(pVia->Chipset == VIA_K8M800)) {
+ if (!(pVia->Chipset == VIA_CLE266) && !(pVia->Chipset == VIA_K8M800) &&
+ !(pVia->Chipset == VIA_PM800)) {
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"[XvMC] Not supported on this chipset.\n");
return;
diff --git a/src/xvmc/unichromeProA/viaLowLevelPro.c b/src/xvmc/unichromeProA/viaLowLevelPro.c
new file mode 100644
index 0000000..6ce58b8
--- /dev/null
+++ b/src/xvmc/unichromeProA/viaLowLevelPro.c
@@ -0,0 +1,1465 @@
+/*****************************************************************************
+ * 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 0x00000001 /* 2D Engine is busy */
+#define VIA_3D_ENG_BUSY 0x00000002 /* 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;
+ }
+}
+
+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);
+}
+
+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 = 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;
+ }
+}
+
+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 = 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 = (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);
+}