diff options
author | Thomas Hellstrom <unichrome@shipmail.org> | 2005-06-28 23:08:46 +0000 |
---|---|---|
committer | Thomas Hellstrom <unichrome@shipmail.org> | 2005-06-28 23:08:46 +0000 |
commit | b8456217647a289d648f026d993165196739d8ab (patch) | |
tree | 2c2d83ac29e089f264357bd394c55aade6c666e9 | |
parent | 24f59186f2f5a187e603aa8762182f562f72be1c (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.c | 3 | ||||
-rw-r--r-- | src/xvmc/unichromeProA/viaLowLevelPro.c | 1465 |
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); +} |