190b17f1bSmrg/*****************************************************************************
290b17f1bSmrg * VIA Unichrome XvMC extension client lib.
390b17f1bSmrg *
490b17f1bSmrg * Copyright (c) 2004 Thomas Hellstr�m. All rights reserved.
590b17f1bSmrg * Copyright (c) 2003 Andreas Robinson. All rights reserved.
690b17f1bSmrg *
790b17f1bSmrg * Permission is hereby granted, free of charge, to any person obtaining a
890b17f1bSmrg * copy of this software and associated documentation files (the "Software"),
990b17f1bSmrg * to deal in the Software without restriction, including without limitation
1090b17f1bSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1190b17f1bSmrg * and/or sell copies of the Software, and to permit persons to whom the
1290b17f1bSmrg * Software is furnished to do so, subject to the following conditions:
1390b17f1bSmrg *
1490b17f1bSmrg * The above copyright notice and this permission notice shall be included in
1590b17f1bSmrg * all copies or substantial portions of the Software.
1690b17f1bSmrg *
1790b17f1bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1890b17f1bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1990b17f1bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2090b17f1bSmrg * AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2190b17f1bSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2290b17f1bSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2390b17f1bSmrg * DEALINGS IN THE SOFTWARE.
2490b17f1bSmrg */
2590b17f1bSmrg
2690b17f1bSmrg/*
2790b17f1bSmrg * Low-level functions that deal directly with the hardware. In the future,
2890b17f1bSmrg * these functions might be implemented in a kernel module. Also, some of them
2990b17f1bSmrg * would benefit from DMA.
3090b17f1bSmrg *
3190b17f1bSmrg * Authors: Andreas Robinson 2003. Thomas Hellstr�m 2004.
3290b17f1bSmrg */
3390b17f1bSmrg
3490b17f1bSmrg#include "viaXvMCPriv.h"
3590b17f1bSmrg#include "viaLowLevel.h"
3690b17f1bSmrg#include <time.h>
3790b17f1bSmrg#include <sys/time.h>
3890b17f1bSmrg#include <stdio.h>
3990b17f1bSmrg
4090b17f1bSmrgtypedef struct
4190b17f1bSmrg{
4290b17f1bSmrg    CARD32 agp_buffer[LL_AGP_CMDBUF_SIZE];
4390b17f1bSmrg    CARD32 pci_buffer[LL_PCI_CMDBUF_SIZE];
4490b17f1bSmrg    unsigned agp_pos;
4590b17f1bSmrg    unsigned pci_pos;
4690b17f1bSmrg    unsigned flip_pos;
4790b17f1bSmrg    int use_agp;
4890b17f1bSmrg    int agp_mode;
4990b17f1bSmrg    int agp_header_start;
5090b17f1bSmrg    int agp_index;
5190b17f1bSmrg    int fd;
5290b17f1bSmrg    drm_context_t *drmcontext;
5390b17f1bSmrg    drmLockPtr hwLock;
5490b17f1bSmrg    drmAddress mmioAddress;
5590b17f1bSmrg    drmAddress fbAddress;
5690b17f1bSmrg    unsigned fbStride;
5790b17f1bSmrg    unsigned fbDepth;
5890b17f1bSmrg    unsigned width;
5990b17f1bSmrg    unsigned height;
6090b17f1bSmrg    unsigned curWaitFlags;
6190b17f1bSmrg    int performLocking;
6290b17f1bSmrg    unsigned errors;
6390b17f1bSmrg    drm_via_mem_t tsMem;
6490b17f1bSmrg    CARD32 tsOffset;
6590b17f1bSmrg    volatile CARD32 *tsP;
6690b17f1bSmrg    CARD32 curTimeStamp;
6790b17f1bSmrg    CARD32 lastReadTimeStamp;
6890b17f1bSmrg    int agpSync;
6990b17f1bSmrg    CARD32 agpSyncTimeStamp;
7090b17f1bSmrg    unsigned chipId;
7190b17f1bSmrg} XvMCLowLevel;
7290b17f1bSmrg
7390b17f1bSmrg/*
7490b17f1bSmrg * For Other architectures than i386 these might have to be modified for
7590b17f1bSmrg * bigendian etc.
7690b17f1bSmrg */
7790b17f1bSmrg
7890b17f1bSmrg#define MPEGIN(xl,reg)							\
7990b17f1bSmrg    *((volatile CARD32 *)(((CARD8 *)(xl)->mmioAddress) + 0xc00 + (reg)))
8090b17f1bSmrg
8190b17f1bSmrg#define VIDIN(ctx,reg)							\
8290b17f1bSmrg    *((volatile CARD32 *)(((CARD8 *)(ctx)->mmioAddress) + (reg)))
8390b17f1bSmrg
8490b17f1bSmrg#define REGIN(ctx,reg)							\
8590b17f1bSmrg    *((volatile CARD32 *)(((CARD8 *)(ctx)->mmioAddress) + 0x0000 + (reg)))
8690b17f1bSmrg
8790b17f1bSmrg#define HQV_CONTROL             0x3D0
8890b17f1bSmrg#define HQV_SRC_STARTADDR_Y     0x3D4
8990b17f1bSmrg#define HQV_SRC_STARTADDR_U     0x3D8
9090b17f1bSmrg#define HQV_SRC_STARTADDR_V     0x3DC
9190b17f1bSmrg#define HQV_MINIFY_DEBLOCK      0x3E8
9290b17f1bSmrg
9390b17f1bSmrg#define HQV_SW_FLIP         0x00000010
9490b17f1bSmrg#define HQV_FLIP_STATUS     0x00000001
9590b17f1bSmrg#define HQV_SUBPIC_FLIP     0x00008000
9690b17f1bSmrg#define HQV_FLIP_ODD        0x00000020
9790b17f1bSmrg#define HQV_DEINTERLACE     0x00010000
9890b17f1bSmrg#define HQV_FIELD_2_FRAME   0x00020000
9990b17f1bSmrg#define HQV_FRAME_2_FIELD   0x00040000
10090b17f1bSmrg#define HQV_FIELD_UV        0x00100000
10190b17f1bSmrg#define HQV_DEBLOCK_HOR     0x00008000
10290b17f1bSmrg#define HQV_DEBLOCK_VER     0x80000000
10390b17f1bSmrg
10490b17f1bSmrg#define V_COMPOSE_MODE          0x298
10590b17f1bSmrg#define V1_COMMAND_FIRE         0x80000000
10690b17f1bSmrg#define V3_COMMAND_FIRE         0x40000000
10790b17f1bSmrg
10890b17f1bSmrg/* SUBPICTURE Registers */
10990b17f1bSmrg#define SUBP_CONTROL_STRIDE     0x3C0
11090b17f1bSmrg#define SUBP_STARTADDR          0x3C4
11190b17f1bSmrg#define RAM_TABLE_CONTROL       0x3C8
11290b17f1bSmrg#define RAM_TABLE_READ          0x3CC
11390b17f1bSmrg
11490b17f1bSmrg/* SUBP_CONTROL_STRIDE              0x3c0 */
11590b17f1bSmrg#define SUBP_HQV_ENABLE             0x00010000
11690b17f1bSmrg#define SUBP_IA44                   0x00020000
11790b17f1bSmrg#define SUBP_AI44                   0x00000000
11890b17f1bSmrg#define SUBP_STRIDE_MASK            0x00001fff
11990b17f1bSmrg#define SUBP_CONTROL_MASK           0x00070000
12090b17f1bSmrg
12190b17f1bSmrg/* RAM_TABLE_CONTROL                0x3c8 */
12290b17f1bSmrg#define RAM_TABLE_RGB_ENABLE        0x00000007
12390b17f1bSmrg
12490b17f1bSmrg#define VIA_REG_STATUS          0x400
12590b17f1bSmrg#define VIA_REG_GEMODE          0x004
12690b17f1bSmrg#define VIA_REG_SRCBASE         0x030
12790b17f1bSmrg#define VIA_REG_DSTBASE         0x034
12890b17f1bSmrg#define VIA_REG_PITCH           0x038
12990b17f1bSmrg#define VIA_REG_SRCCOLORKEY     0x01C
13090b17f1bSmrg#define VIA_REG_KEYCONTROL      0x02C
13190b17f1bSmrg#define VIA_REG_SRCPOS          0x008
13290b17f1bSmrg#define VIA_REG_DSTPOS          0x00C
13390b17f1bSmrg#define VIA_REG_GECMD           0x000
13490b17f1bSmrg#define VIA_REG_DIMENSION       0x010  /* width and height */
13590b17f1bSmrg#define VIA_REG_FGCOLOR         0x018
13690b17f1bSmrg
13790b17f1bSmrg#define VIA_VR_QUEUE_BUSY       0x00020000	/* Virtual Queue is busy */
13890b17f1bSmrg#define VIA_CMD_RGTR_BUSY       0x00000080	/* Command Regulator is busy */
13990b17f1bSmrg#define VIA_2D_ENG_BUSY         0x00000002	/* 2D Engine is busy */
14090b17f1bSmrg#define VIA_3D_ENG_BUSY         0x00000001	/* 3D Engine is busy */
14190b17f1bSmrg#define VIA_GEM_8bpp            0x00000000
14290b17f1bSmrg#define VIA_GEM_16bpp           0x00000100
14390b17f1bSmrg#define VIA_GEM_32bpp           0x00000300
14490b17f1bSmrg#define VIA_GEC_BLT             0x00000001
14590b17f1bSmrg#define VIA_PITCH_ENABLE        0x80000000
14690b17f1bSmrg#define VIA_GEC_INCX            0x00000000
14790b17f1bSmrg#define VIA_GEC_DECY            0x00004000
14890b17f1bSmrg#define VIA_GEC_INCY            0x00000000
14990b17f1bSmrg#define VIA_GEC_DECX            0x00008000
15090b17f1bSmrg#define VIA_GEC_FIXCOLOR_PAT    0x00002000
15190b17f1bSmrg
15290b17f1bSmrg#define VIA_BLIT_CLEAR 0x00
15390b17f1bSmrg#define VIA_BLIT_COPY 0xCC
15490b17f1bSmrg#define VIA_BLIT_FILL 0xF0
15590b17f1bSmrg#define VIA_BLIT_SET 0xFF
15690b17f1bSmrg
15790b17f1bSmrg#define VIA_SYNCWAITTIMEOUT 50000      /* Might be a bit conservative */
15890b17f1bSmrg#define VIA_DMAWAITTIMEOUT 150000
15990b17f1bSmrg#define VIA_VIDWAITTIMEOUT 50000
16090b17f1bSmrg#define VIA_XVMC_DECODERTIMEOUT 50000  /*(microseconds) */
16190b17f1bSmrg
16290b17f1bSmrg#define H1_ADDR(val) (((val) >> 2) | 0xF0000000)
16390b17f1bSmrg#define WAITFLAGS(xl, flags)			\
16490b17f1bSmrg    (xl)->curWaitFlags |= (flags)
16590b17f1bSmrg#define BEGIN_RING_AGP(xl,size)						\
16690b17f1bSmrg    do {								\
16790b17f1bSmrg	if ((xl)->agp_pos > (LL_AGP_CMDBUF_SIZE-(size))) {		\
16890b17f1bSmrg	    agpFlush(xl);						\
16990b17f1bSmrg	}								\
17090b17f1bSmrg    } while(0)
17190b17f1bSmrg#define OUT_RING_AGP(xl, val)			\
17290b17f1bSmrg    (xl)->agp_buffer[(xl)->agp_pos++] = (val)
17390b17f1bSmrg#define OUT_RING_QW_AGP(xl, val1, val2)			\
17490b17f1bSmrg    do {						\
17590b17f1bSmrg	(xl)->agp_buffer[(xl)->agp_pos++] = (val1);	\
17690b17f1bSmrg	(xl)->agp_buffer[(xl)->agp_pos++] = (val2);	\
17790b17f1bSmrg    } while (0)
17890b17f1bSmrg
17990b17f1bSmrg#define LL_HW_LOCK(xl)							\
18090b17f1bSmrg    do {								\
18190b17f1bSmrg	DRM_LOCK((xl)->fd,(xl)->hwLock,*(xl)->drmcontext,0);		\
18290b17f1bSmrg    } while(0);
18390b17f1bSmrg#define LL_HW_UNLOCK(xl)					\
18490b17f1bSmrg    do {							\
18590b17f1bSmrg	DRM_UNLOCK((xl)->fd,(xl)->hwLock,*(xl)->drmcontext);	\
18690b17f1bSmrg    } while(0);
18790b17f1bSmrg
18890b17f1bSmrg/*
18990b17f1bSmrg * We want to have two concurrent types of thread taking the hardware
19090b17f1bSmrg * lock simulataneously. One is the video out thread that needs immediate
19190b17f1bSmrg * access to flip an image. The other is everything else which may have
19290b17f1bSmrg * the lock for quite some time. This is only so the video out thread can
19390b17f1bSmrg * sneak in and display an image while other resources are busy.
19490b17f1bSmrg */
19590b17f1bSmrg
19690b17f1bSmrgvoid
19790b17f1bSmrghwlLock(void *xlp, int videoLock)
19890b17f1bSmrg{
19990b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
20090b17f1bSmrg
20190b17f1bSmrg    LL_HW_LOCK(xl);
20290b17f1bSmrg}
20390b17f1bSmrg
20490b17f1bSmrgvoid
20590b17f1bSmrghwlUnlock(void *xlp, int videoLock)
20690b17f1bSmrg{
20790b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
20890b17f1bSmrg
20990b17f1bSmrg    LL_HW_UNLOCK(xl);
21090b17f1bSmrg}
21190b17f1bSmrg
21290b17f1bSmrgstatic unsigned
21390b17f1bSmrgtimeDiff(struct timeval *now, struct timeval *then)
21490b17f1bSmrg{
21590b17f1bSmrg    return (now->tv_usec >= then->tv_usec) ?
21690b17f1bSmrg	now->tv_usec - then->tv_usec :
21790b17f1bSmrg	1000000 - (then->tv_usec - now->tv_usec);
21890b17f1bSmrg}
21990b17f1bSmrg
22090b17f1bSmrgvoid
22190b17f1bSmrgsetAGPSyncLowLevel(void *xlp, int val, CARD32 timeStamp)
22290b17f1bSmrg{
22390b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
22490b17f1bSmrg
22590b17f1bSmrg    xl->agpSync = val;
22690b17f1bSmrg    xl->agpSyncTimeStamp = timeStamp;
22790b17f1bSmrg}
22890b17f1bSmrg
22990b17f1bSmrgCARD32
23090b17f1bSmrgviaDMATimeStampLowLevel(void *xlp)
23190b17f1bSmrg{
23290b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
23390b17f1bSmrg
23490b17f1bSmrg    if (xl->use_agp) {
23590b17f1bSmrg	viaBlit(xl, 32, xl->tsOffset, 1, xl->tsOffset, 1, 1, 1, 0, 0,
23690b17f1bSmrg	    VIABLIT_FILL, xl->curTimeStamp);
23790b17f1bSmrg	return xl->curTimeStamp++;
23890b17f1bSmrg    }
23990b17f1bSmrg    return 0;
24090b17f1bSmrg}
24190b17f1bSmrg
24290b17f1bSmrgstatic void
24390b17f1bSmrgviaDMAWaitTimeStamp(XvMCLowLevel * xl, CARD32 timeStamp, int doSleep)
24490b17f1bSmrg{
24590b17f1bSmrg    struct timeval now, then;
24690b17f1bSmrg    struct timezone here;
24790b17f1bSmrg    struct timespec sleep, rem;
24890b17f1bSmrg
24990b17f1bSmrg    if (xl->use_agp && (timeStamp > xl->lastReadTimeStamp)) {
25090b17f1bSmrg	sleep.tv_nsec = 1;
25190b17f1bSmrg	sleep.tv_sec = 0;
25290b17f1bSmrg	here.tz_minuteswest = 0;
25390b17f1bSmrg	here.tz_dsttime = 0;
25490b17f1bSmrg	gettimeofday(&then, &here);
25590b17f1bSmrg
25690b17f1bSmrg	while (timeStamp > (xl->lastReadTimeStamp = *xl->tsP)) {
25790b17f1bSmrg	    gettimeofday(&now, &here);
25890b17f1bSmrg	    if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
25990b17f1bSmrg		if ((timeStamp > (xl->lastReadTimeStamp = *xl->tsP))) {
26090b17f1bSmrg		    xl->errors |= LL_DMA_TIMEDOUT;
26190b17f1bSmrg		    break;
26290b17f1bSmrg		}
26390b17f1bSmrg	    }
26490b17f1bSmrg	    if (doSleep)
26590b17f1bSmrg		nanosleep(&sleep, &rem);
26690b17f1bSmrg	}
26790b17f1bSmrg    }
26890b17f1bSmrg}
26990b17f1bSmrg
27090b17f1bSmrgstatic int
27190b17f1bSmrgviaDMAInitTimeStamp(XvMCLowLevel * xl)
27290b17f1bSmrg{
27390b17f1bSmrg    int ret = 0;
27490b17f1bSmrg
27590b17f1bSmrg    if (xl->use_agp) {
27690b17f1bSmrg	xl->tsMem.context = *(xl->drmcontext);
27790b17f1bSmrg	xl->tsMem.size = 64;
27890b17f1bSmrg	xl->tsMem.type = VIA_MEM_VIDEO;
27990b17f1bSmrg	if ((ret = drmCommandWriteRead(xl->fd, DRM_VIA_ALLOCMEM, &xl->tsMem,
28090b17f1bSmrg		sizeof(xl->tsMem))) < 0)
28190b17f1bSmrg	    return ret;
28290b17f1bSmrg	if (xl->tsMem.size != 64)
28390b17f1bSmrg	    return -1;
28490b17f1bSmrg	xl->tsOffset = (xl->tsMem.offset + 31) & ~31;
28590b17f1bSmrg	xl->tsP = (CARD32 *) xl->fbAddress + (xl->tsOffset >> 2);
28690b17f1bSmrg	xl->curTimeStamp = 1;
28790b17f1bSmrg	*xl->tsP = 0;
28890b17f1bSmrg    }
28990b17f1bSmrg    return 0;
29090b17f1bSmrg}
29190b17f1bSmrg
29290b17f1bSmrgstatic int
29390b17f1bSmrgviaDMACleanupTimeStamp(XvMCLowLevel * xl)
29490b17f1bSmrg{
29590b17f1bSmrg
29690b17f1bSmrg    if (!(xl->tsMem.size) || !xl->use_agp)
29790b17f1bSmrg	return 0;
29890b17f1bSmrg    return drmCommandWrite(xl->fd, DRM_VIA_FREEMEM, &xl->tsMem,
29990b17f1bSmrg	sizeof(xl->tsMem));
30090b17f1bSmrg}
30190b17f1bSmrg
30290b17f1bSmrgstatic CARD32
30390b17f1bSmrgviaMpegGetStatus(XvMCLowLevel * xl)
30490b17f1bSmrg{
30590b17f1bSmrg    return MPEGIN(xl, 0x54);
30690b17f1bSmrg}
30790b17f1bSmrg
30890b17f1bSmrgstatic int
30990b17f1bSmrgviaMpegIsBusy(XvMCLowLevel * xl, CARD32 mask, CARD32 idle)
31090b17f1bSmrg{
31190b17f1bSmrg    CARD32 tmp = viaMpegGetStatus(xl);
31290b17f1bSmrg
31390b17f1bSmrg    /*
31490b17f1bSmrg     * Error detected.
31590b17f1bSmrg     * FIXME: Are errors really shown when error concealment is on?
31690b17f1bSmrg     */
31790b17f1bSmrg
31890b17f1bSmrg    if (tmp & 0x70)
31990b17f1bSmrg	return 0;
32090b17f1bSmrg
32190b17f1bSmrg    return (tmp & mask) != idle;
32290b17f1bSmrg}
32390b17f1bSmrg
32490b17f1bSmrgstatic void
32590b17f1bSmrgsyncDMA(XvMCLowLevel * xl, unsigned int doSleep)
32690b17f1bSmrg{
32790b17f1bSmrg
32890b17f1bSmrg    /*
32990b17f1bSmrg     * Ideally, we'd like to have an interrupt wait here, but, according to second hand
33090b17f1bSmrg     * information, the hardware does not support this, although earlier S3 chips do that.
33190b17f1bSmrg     * It is therefore not implemented into the DRM, and we'll do a user space wait here.
33290b17f1bSmrg     */
33390b17f1bSmrg
33490b17f1bSmrg    struct timeval now, then;
33590b17f1bSmrg    struct timezone here;
33690b17f1bSmrg    struct timespec sleep, rem;
33790b17f1bSmrg
33890b17f1bSmrg    sleep.tv_nsec = 1;
33990b17f1bSmrg    sleep.tv_sec = 0;
34090b17f1bSmrg    here.tz_minuteswest = 0;
34190b17f1bSmrg    here.tz_dsttime = 0;
34290b17f1bSmrg    gettimeofday(&then, &here);
34390b17f1bSmrg    while (!(REGIN(xl, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY)) {
34490b17f1bSmrg	gettimeofday(&now, &here);
34590b17f1bSmrg	if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
34690b17f1bSmrg	    if (!(REGIN(xl, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY)) {
34790b17f1bSmrg		xl->errors |= LL_DMA_TIMEDOUT;
34890b17f1bSmrg		break;
34990b17f1bSmrg	    }
35090b17f1bSmrg	}
35190b17f1bSmrg	if (doSleep)
35290b17f1bSmrg	    nanosleep(&sleep, &rem);
35390b17f1bSmrg    }
35490b17f1bSmrg    while (REGIN(xl, VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY) {
35590b17f1bSmrg	gettimeofday(&now, &here);
35690b17f1bSmrg	if (timeDiff(&now, &then) > VIA_DMAWAITTIMEOUT) {
35790b17f1bSmrg	    if (REGIN(xl, VIA_REG_STATUS) & VIA_CMD_RGTR_BUSY) {
35890b17f1bSmrg		xl->errors |= LL_DMA_TIMEDOUT;
35990b17f1bSmrg		break;
36090b17f1bSmrg	    }
36190b17f1bSmrg	}
36290b17f1bSmrg	if (doSleep)
36390b17f1bSmrg	    nanosleep(&sleep, &rem);
36490b17f1bSmrg    }
36590b17f1bSmrg}
36690b17f1bSmrg
36790b17f1bSmrgstatic void
36890b17f1bSmrgsyncVideo(XvMCLowLevel * xl, unsigned int doSleep)
36990b17f1bSmrg{
37090b17f1bSmrg    /*
37190b17f1bSmrg     * Wait for HQV completion. Nothing strange here. We assume that the HQV
37290b17f1bSmrg     * Handles syncing to the V1 / V3 engines by itself. It should be safe to
37390b17f1bSmrg     * always wait for SUBPIC_FLIP completion although subpictures are not
37490b17f1bSmrg     * always used.
37590b17f1bSmrg     */
37690b17f1bSmrg
37790b17f1bSmrg    struct timeval now, then;
37890b17f1bSmrg    struct timezone here;
37990b17f1bSmrg    struct timespec sleep, rem;
38090b17f1bSmrg
38190b17f1bSmrg    sleep.tv_nsec = 1;
38290b17f1bSmrg    sleep.tv_sec = 0;
38390b17f1bSmrg    here.tz_minuteswest = 0;
38490b17f1bSmrg    here.tz_dsttime = 0;
38590b17f1bSmrg    gettimeofday(&then, &here);
38690b17f1bSmrg    while (VIDIN(xl, HQV_CONTROL) & (HQV_SW_FLIP | HQV_SUBPIC_FLIP)) {
38790b17f1bSmrg	gettimeofday(&now, &here);
38890b17f1bSmrg	if (timeDiff(&now, &then) > VIA_SYNCWAITTIMEOUT) {
38990b17f1bSmrg	    if (VIDIN(xl, HQV_CONTROL) & (HQV_SW_FLIP | HQV_SUBPIC_FLIP)) {
39090b17f1bSmrg		xl->errors |= LL_VIDEO_TIMEDOUT;
39190b17f1bSmrg		break;
39290b17f1bSmrg	    }
39390b17f1bSmrg	}
39490b17f1bSmrg	if (doSleep)
39590b17f1bSmrg	    nanosleep(&sleep, &rem);
39690b17f1bSmrg    }
39790b17f1bSmrg}
39890b17f1bSmrg
39990b17f1bSmrgstatic void
40090b17f1bSmrgsyncAccel(XvMCLowLevel * xl, unsigned int mode, unsigned int doSleep)
40190b17f1bSmrg{
40290b17f1bSmrg    struct timeval now, then;
40390b17f1bSmrg    struct timezone here;
40490b17f1bSmrg    struct timespec sleep, rem;
40590b17f1bSmrg    CARD32 mask = ((mode & LL_MODE_2D) ? VIA_2D_ENG_BUSY : 0) |
40690b17f1bSmrg	((mode & LL_MODE_3D) ? VIA_3D_ENG_BUSY : 0);
40790b17f1bSmrg
40890b17f1bSmrg    sleep.tv_nsec = 1;
40990b17f1bSmrg    sleep.tv_sec = 0;
41090b17f1bSmrg    here.tz_minuteswest = 0;
41190b17f1bSmrg    here.tz_dsttime = 0;
41290b17f1bSmrg    gettimeofday(&then, &here);
41390b17f1bSmrg    while (REGIN(xl, VIA_REG_STATUS) & mask) {
41490b17f1bSmrg	gettimeofday(&now, &here);
41590b17f1bSmrg	if (timeDiff(&now, &then) > VIA_SYNCWAITTIMEOUT) {
41690b17f1bSmrg	    if (REGIN(xl, VIA_REG_STATUS) & mask) {
41790b17f1bSmrg		xl->errors |= LL_ACCEL_TIMEDOUT;
41890b17f1bSmrg		break;
41990b17f1bSmrg	    }
42090b17f1bSmrg	}
42190b17f1bSmrg	if (doSleep)
42290b17f1bSmrg	    nanosleep(&sleep, &rem);
42390b17f1bSmrg    }
42490b17f1bSmrg}
42590b17f1bSmrg
42690b17f1bSmrgstatic void
42790b17f1bSmrgsyncMpeg(XvMCLowLevel * xl, unsigned int mode, unsigned int doSleep)
42890b17f1bSmrg{
42990b17f1bSmrg    /*
43090b17f1bSmrg     * Ideally, we'd like to have an interrupt wait here, but from information from VIA
43190b17f1bSmrg     * at least the MPEG completion interrupt is broken on the CLE266, which was
43290b17f1bSmrg     * discovered during validation of the chip.
43390b17f1bSmrg     */
43490b17f1bSmrg
43590b17f1bSmrg    struct timeval now, then;
43690b17f1bSmrg    struct timezone here;
43790b17f1bSmrg    struct timespec sleep, rem;
43890b17f1bSmrg    CARD32 busyMask = 0;
43990b17f1bSmrg    CARD32 idleVal = 0;
44090b17f1bSmrg    CARD32 ret;
44190b17f1bSmrg
44290b17f1bSmrg    sleep.tv_nsec = 1;
44390b17f1bSmrg    sleep.tv_sec = 0;
44490b17f1bSmrg    here.tz_minuteswest = 0;
44590b17f1bSmrg    here.tz_dsttime = 0;
44690b17f1bSmrg    gettimeofday(&then, &here);
44790b17f1bSmrg    if (mode & LL_MODE_DECODER_SLICE) {
44890b17f1bSmrg	busyMask = VIA_SLICEBUSYMASK;
44990b17f1bSmrg	idleVal = VIA_SLICEIDLEVAL;
45090b17f1bSmrg    }
45190b17f1bSmrg    if (mode & LL_MODE_DECODER_IDLE) {
45290b17f1bSmrg	busyMask |= VIA_BUSYMASK;
45390b17f1bSmrg	idleVal = VIA_IDLEVAL;
45490b17f1bSmrg    }
45590b17f1bSmrg    while (viaMpegIsBusy(xl, busyMask, idleVal)) {
45690b17f1bSmrg	gettimeofday(&now, &here);
45790b17f1bSmrg	if (timeDiff(&now, &then) > VIA_XVMC_DECODERTIMEOUT) {
45890b17f1bSmrg	    if (viaMpegIsBusy(xl, busyMask, idleVal)) {
45990b17f1bSmrg		xl->errors |= LL_DECODER_TIMEDOUT;
46090b17f1bSmrg	    }
46190b17f1bSmrg	    break;
46290b17f1bSmrg	}
46390b17f1bSmrg	if (doSleep)
46490b17f1bSmrg	    nanosleep(&sleep, &rem);
46590b17f1bSmrg    }
46690b17f1bSmrg
46790b17f1bSmrg    ret = viaMpegGetStatus(xl);
46890b17f1bSmrg    if (ret & 0x70) {
46990b17f1bSmrg	xl->errors |= ((ret & 0x70) >> 3);
47090b17f1bSmrg    }
47190b17f1bSmrg    return;
47290b17f1bSmrg}
47390b17f1bSmrg
47490b17f1bSmrgstatic void
47590b17f1bSmrgpciFlush(XvMCLowLevel * xl)
47690b17f1bSmrg{
47790b17f1bSmrg    int ret;
47890b17f1bSmrg    drm_via_cmdbuffer_t b;
47990b17f1bSmrg    unsigned mode = xl->curWaitFlags;
48090b17f1bSmrg
48190b17f1bSmrg    b.buf = (char *)xl->pci_buffer;
48290b17f1bSmrg    b.size = xl->pci_pos * sizeof(CARD32);
48390b17f1bSmrg    if (xl->performLocking)
48490b17f1bSmrg	hwlLock(xl, 0);
48590b17f1bSmrg    if ((mode != LL_MODE_VIDEO) && (mode != 0))
48690b17f1bSmrg	syncDMA(xl, 0);
48790b17f1bSmrg    if ((mode & LL_MODE_2D) || (mode & LL_MODE_3D))
48890b17f1bSmrg	syncAccel(xl, mode, 0);
48990b17f1bSmrg    if (mode & LL_MODE_VIDEO)
49090b17f1bSmrg	syncVideo(xl, 0);
49190b17f1bSmrg    if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE))
49290b17f1bSmrg	syncMpeg(xl, mode, 0);
49390b17f1bSmrg    ret = drmCommandWrite(xl->fd, DRM_VIA_PCICMD, &b, sizeof(b));
49490b17f1bSmrg    if (xl->performLocking)
49590b17f1bSmrg	hwlUnlock(xl, 0);
49690b17f1bSmrg    if (ret) {
49790b17f1bSmrg	xl->errors |= LL_PCI_COMMAND_ERR;
49890b17f1bSmrg    }
49990b17f1bSmrg    xl->pci_pos = 0;
50090b17f1bSmrg    xl->curWaitFlags = 0;
50190b17f1bSmrg}
50290b17f1bSmrg
50390b17f1bSmrgstatic void
50490b17f1bSmrgagpFlush(XvMCLowLevel * xl)
50590b17f1bSmrg{
50690b17f1bSmrg    drm_via_cmdbuffer_t b;
50790b17f1bSmrg    int ret;
50890b17f1bSmrg
50990b17f1bSmrg    if (xl->use_agp) {
51090b17f1bSmrg	b.buf = (char *)xl->agp_buffer;
51190b17f1bSmrg	b.size = xl->agp_pos * sizeof(CARD32);
51290b17f1bSmrg	if (xl->agpSync) {
51390b17f1bSmrg	    syncXvMCLowLevel(xl, LL_MODE_DECODER_IDLE, 1,
51490b17f1bSmrg		xl->agpSyncTimeStamp);
51590b17f1bSmrg	    xl->agpSync = 0;
51690b17f1bSmrg	}
51790b17f1bSmrg	if (xl->performLocking)
51890b17f1bSmrg	    hwlLock(xl, 0);
51990b17f1bSmrg	do {
52090b17f1bSmrg	    ret = drmCommandWrite(xl->fd, DRM_VIA_CMDBUFFER, &b, sizeof(b));
52190b17f1bSmrg	} while (-EAGAIN == ret);
52290b17f1bSmrg	if (xl->performLocking)
52390b17f1bSmrg	    hwlUnlock(xl, 0);
52490b17f1bSmrg
52590b17f1bSmrg	if (ret) {
52690b17f1bSmrg	    xl->errors |= LL_AGP_COMMAND_ERR;
52790b17f1bSmrg	} else {
52890b17f1bSmrg	    xl->agp_pos = 0;
52990b17f1bSmrg	}
53090b17f1bSmrg	xl->curWaitFlags &= LL_MODE_VIDEO;
53190b17f1bSmrg    } else {
53290b17f1bSmrg	unsigned mode = xl->curWaitFlags;
53390b17f1bSmrg
53490b17f1bSmrg	b.buf = (char *)xl->agp_buffer;
53590b17f1bSmrg	b.size = xl->agp_pos * sizeof(CARD32);
53690b17f1bSmrg	if (xl->performLocking)
53790b17f1bSmrg	    hwlLock(xl, 0);
53890b17f1bSmrg	if ((mode != LL_MODE_VIDEO) && (mode != 0))
53990b17f1bSmrg	    syncDMA(xl, 0);
54090b17f1bSmrg	if ((mode & LL_MODE_2D) || (mode & LL_MODE_3D))
54190b17f1bSmrg	    syncAccel(xl, mode, 0);
54290b17f1bSmrg	if (mode & LL_MODE_VIDEO)
54390b17f1bSmrg	    syncVideo(xl, 0);
54490b17f1bSmrg	if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE))
54590b17f1bSmrg	    syncMpeg(xl, mode, 0);
54690b17f1bSmrg	ret = drmCommandWrite(xl->fd, DRM_VIA_PCICMD, &b, sizeof(b));
54790b17f1bSmrg	if (xl->performLocking)
54890b17f1bSmrg	    hwlUnlock(xl, 0);
54990b17f1bSmrg	if (ret) {
55090b17f1bSmrg	    xl->errors |= LL_PCI_COMMAND_ERR;
55190b17f1bSmrg	}
55290b17f1bSmrg	xl->agp_pos = 0;
55390b17f1bSmrg	xl->curWaitFlags = 0;
55490b17f1bSmrg    }
55590b17f1bSmrg}
55690b17f1bSmrg
55790b17f1bSmrgunsigned
55890b17f1bSmrgflushXvMCLowLevel(void *xlp)
55990b17f1bSmrg{
56090b17f1bSmrg    unsigned errors;
56190b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
56290b17f1bSmrg
56390b17f1bSmrg    if (xl->pci_pos)
56490b17f1bSmrg	pciFlush(xl);
56590b17f1bSmrg    if (xl->agp_pos)
56690b17f1bSmrg	agpFlush(xl);
56790b17f1bSmrg    errors = xl->errors;
56890b17f1bSmrg    xl->errors = 0;
56990b17f1bSmrg    return errors;
57090b17f1bSmrg}
57190b17f1bSmrg
57290b17f1bSmrgvoid
57390b17f1bSmrgflushPCIXvMCLowLevel(void *xlp)
57490b17f1bSmrg{
57590b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
57690b17f1bSmrg
57790b17f1bSmrg    if (xl->pci_pos)
57890b17f1bSmrg	pciFlush(xl);
57990b17f1bSmrg    if (!xl->use_agp && xl->agp_pos)
58090b17f1bSmrg	agpFlush(xl);
58190b17f1bSmrg}
58290b17f1bSmrg
58390b17f1bSmrg__inline static void
58490b17f1bSmrgpciCommand(XvMCLowLevel * xl, unsigned offset, unsigned value, unsigned flags)
58590b17f1bSmrg{
58690b17f1bSmrg    if (xl->pci_pos > (LL_PCI_CMDBUF_SIZE - 2))
58790b17f1bSmrg	pciFlush(xl);
58890b17f1bSmrg    if (flags)
58990b17f1bSmrg	xl->curWaitFlags |= flags;
59090b17f1bSmrg    xl->pci_buffer[xl->pci_pos++] = (offset >> 2) | 0xF0000000;
59190b17f1bSmrg    xl->pci_buffer[xl->pci_pos++] = value;
59290b17f1bSmrg}
59390b17f1bSmrg
59490b17f1bSmrgvoid
59590b17f1bSmrgviaMpegSetSurfaceStride(void *xlp, ViaXvMCContext * ctx)
59690b17f1bSmrg{
59790b17f1bSmrg    CARD32 y_stride = ctx->yStride;
59890b17f1bSmrg    CARD32 uv_stride = y_stride >> 1;
59990b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
60090b17f1bSmrg
60190b17f1bSmrg    BEGIN_RING_AGP(xl, 2);
60290b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc50),
60390b17f1bSmrg	(y_stride >> 3) | ((uv_stride >> 3) << 16));
60490b17f1bSmrg    WAITFLAGS(xl, LL_MODE_DECODER_IDLE);
60590b17f1bSmrg}
60690b17f1bSmrg
60790b17f1bSmrgvoid
60890b17f1bSmrgviaVideoSetSWFLipLocked(void *xlp, unsigned yOffs, unsigned uOffs,
60990b17f1bSmrg    unsigned vOffs, unsigned yStride, unsigned uvStride)
61090b17f1bSmrg{
61190b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
61290b17f1bSmrg
61390b17f1bSmrg    pciCommand(xl, HQV_SRC_STARTADDR_Y, yOffs, LL_MODE_VIDEO);
61490b17f1bSmrg    pciCommand(xl, HQV_SRC_STARTADDR_U, uOffs, 0);
61590b17f1bSmrg    pciCommand(xl, HQV_SRC_STARTADDR_V, vOffs, 0);
61690b17f1bSmrg}
61790b17f1bSmrg
61890b17f1bSmrgvoid
61990b17f1bSmrgviaVideoSWFlipLocked(void *xlp, unsigned flags, int progressiveSequence)
62090b17f1bSmrg{
62190b17f1bSmrg    CARD32 andWd, orWd;
62290b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
62390b17f1bSmrg
62490b17f1bSmrg    andWd = 0;
62590b17f1bSmrg    orWd = 0;
62690b17f1bSmrg
62790b17f1bSmrg    if ((flags & XVMC_FRAME_PICTURE) == XVMC_BOTTOM_FIELD) {
62890b17f1bSmrg	andWd = 0xFFFFFFFFU;
62990b17f1bSmrg	orWd = HQV_FIELD_UV |
63090b17f1bSmrg	    HQV_DEINTERLACE |
63190b17f1bSmrg	    HQV_FIELD_2_FRAME |
63290b17f1bSmrg	    HQV_FRAME_2_FIELD |
63390b17f1bSmrg	    HQV_SW_FLIP | HQV_FLIP_ODD | HQV_FLIP_STATUS | HQV_SUBPIC_FLIP;
63490b17f1bSmrg    } else if ((flags & XVMC_FRAME_PICTURE) == XVMC_TOP_FIELD) {
63590b17f1bSmrg	andWd = ~HQV_FLIP_ODD;
63690b17f1bSmrg	orWd = HQV_FIELD_UV |
63790b17f1bSmrg	    HQV_DEINTERLACE |
63890b17f1bSmrg	    HQV_FIELD_2_FRAME |
63990b17f1bSmrg	    HQV_FRAME_2_FIELD |
64090b17f1bSmrg	    HQV_SW_FLIP | HQV_FLIP_STATUS | HQV_SUBPIC_FLIP;
64190b17f1bSmrg    } else if ((flags & XVMC_FRAME_PICTURE) == XVMC_FRAME_PICTURE) {
64290b17f1bSmrg	andWd = ~(HQV_DEINTERLACE |
64390b17f1bSmrg	    HQV_FRAME_2_FIELD | HQV_FIELD_2_FRAME | HQV_FIELD_UV);
64490b17f1bSmrg	orWd = HQV_SW_FLIP | HQV_FLIP_STATUS | HQV_SUBPIC_FLIP;
64590b17f1bSmrg    }
64690b17f1bSmrg    if (progressiveSequence) {
64790b17f1bSmrg	andWd &= ~HQV_FIELD_UV;
64890b17f1bSmrg	orWd &= ~HQV_FIELD_UV;
64990b17f1bSmrg    }
65090b17f1bSmrg
65190b17f1bSmrg    pciCommand(xl, HQV_CONTROL, (VIDIN(xl,
65290b17f1bSmrg		HQV_CONTROL) & andWd) | orWd, 0);
65390b17f1bSmrg}
65490b17f1bSmrg
65590b17f1bSmrgvoid
65690b17f1bSmrgviaMpegSetFB(void *xlp, unsigned i,
65790b17f1bSmrg    unsigned yOffs, unsigned uOffs, unsigned vOffs)
65890b17f1bSmrg{
65990b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
66090b17f1bSmrg
66190b17f1bSmrg    i *= 12;
66290b17f1bSmrg    BEGIN_RING_AGP(xl, 6);
66390b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc20 + i), yOffs >> 3);
66490b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc24 + i), uOffs >> 3);
66590b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc28 + i), vOffs >> 3);
66690b17f1bSmrg    WAITFLAGS(xl, LL_MODE_DECODER_IDLE);
66790b17f1bSmrg}
66890b17f1bSmrg
66990b17f1bSmrgvoid
67090b17f1bSmrgviaMpegBeginPicture(void *xlp, ViaXvMCContext * ctx,
67190b17f1bSmrg    unsigned width, unsigned height, const XvMCMpegControl * control)
67290b17f1bSmrg{
67390b17f1bSmrg
67490b17f1bSmrg    unsigned j, mb_width, mb_height;
67590b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
67690b17f1bSmrg
67790b17f1bSmrg    mb_width = (width + 15) >> 4;
67890b17f1bSmrg
67990b17f1bSmrg    mb_height =
68090b17f1bSmrg	((control->mpeg_coding == XVMC_MPEG_2) &&
68190b17f1bSmrg	(control->flags & XVMC_PROGRESSIVE_SEQUENCE)) ?
68290b17f1bSmrg	2 * ((height + 31) >> 5) : (((height + 15) >> 4));
68390b17f1bSmrg
68490b17f1bSmrg    BEGIN_RING_AGP(xl, 144);
68590b17f1bSmrg    WAITFLAGS(xl, LL_MODE_DECODER_IDLE);
68690b17f1bSmrg
68790b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc00),
68890b17f1bSmrg	((control->picture_structure & XVMC_FRAME_PICTURE) << 2) |
68990b17f1bSmrg	((control->picture_coding_type & 3) << 4) |
69090b17f1bSmrg	((control->flags & XVMC_ALTERNATE_SCAN) ? (1 << 6) : 0));
69190b17f1bSmrg
69290b17f1bSmrg    if (!(ctx->intraLoaded)) {
69390b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc5c), 0);
69490b17f1bSmrg	for (j = 0; j < 64; j += 4) {
69590b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(0xc60),
69690b17f1bSmrg		ctx->intra_quantiser_matrix[j] |
69790b17f1bSmrg		(ctx->intra_quantiser_matrix[j + 1] << 8) |
69890b17f1bSmrg		(ctx->intra_quantiser_matrix[j + 2] << 16) |
69990b17f1bSmrg		(ctx->intra_quantiser_matrix[j + 3] << 24));
70090b17f1bSmrg	}
70190b17f1bSmrg	ctx->intraLoaded = 1;
70290b17f1bSmrg    }
70390b17f1bSmrg
70490b17f1bSmrg    if (!(ctx->nonIntraLoaded)) {
70590b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc5c), 1);
70690b17f1bSmrg	for (j = 0; j < 64; j += 4) {
70790b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(0xc60),
70890b17f1bSmrg		ctx->non_intra_quantiser_matrix[j] |
70990b17f1bSmrg		(ctx->non_intra_quantiser_matrix[j + 1] << 8) |
71090b17f1bSmrg		(ctx->non_intra_quantiser_matrix[j + 2] << 16) |
71190b17f1bSmrg		(ctx->non_intra_quantiser_matrix[j + 3] << 24));
71290b17f1bSmrg	}
71390b17f1bSmrg	ctx->nonIntraLoaded = 1;
71490b17f1bSmrg    }
71590b17f1bSmrg
71690b17f1bSmrg    if (!(ctx->chromaIntraLoaded)) {
71790b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc5c), 2);
71890b17f1bSmrg	for (j = 0; j < 64; j += 4) {
71990b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(0xc60),
72090b17f1bSmrg		ctx->chroma_intra_quantiser_matrix[j] |
72190b17f1bSmrg		(ctx->chroma_intra_quantiser_matrix[j + 1] << 8) |
72290b17f1bSmrg		(ctx->chroma_intra_quantiser_matrix[j + 2] << 16) |
72390b17f1bSmrg		(ctx->chroma_intra_quantiser_matrix[j + 3] << 24));
72490b17f1bSmrg	}
72590b17f1bSmrg	ctx->chromaIntraLoaded = 1;
72690b17f1bSmrg    }
72790b17f1bSmrg
72890b17f1bSmrg    if (!(ctx->chromaNonIntraLoaded)) {
72990b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc5c), 3);
73090b17f1bSmrg	for (j = 0; j < 64; j += 4) {
73190b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(0xc60),
73290b17f1bSmrg		ctx->chroma_non_intra_quantiser_matrix[j] |
73390b17f1bSmrg		(ctx->chroma_non_intra_quantiser_matrix[j + 1] << 8) |
73490b17f1bSmrg		(ctx->chroma_non_intra_quantiser_matrix[j + 2] << 16) |
73590b17f1bSmrg		(ctx->chroma_non_intra_quantiser_matrix[j + 3] << 24));
73690b17f1bSmrg	}
73790b17f1bSmrg	ctx->chromaNonIntraLoaded = 1;
73890b17f1bSmrg    }
73990b17f1bSmrg
74090b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc90),
74190b17f1bSmrg	((mb_width * mb_height) & 0x3fff) |
74290b17f1bSmrg	((control->flags & XVMC_PRED_DCT_FRAME) ? (1 << 14) : 0) |
74390b17f1bSmrg	((control->flags & XVMC_TOP_FIELD_FIRST) ? (1 << 15) : 0) |
74490b17f1bSmrg	((control->mpeg_coding == XVMC_MPEG_2) ? (1 << 16) : 0) |
74590b17f1bSmrg	((mb_width & 0xff) << 18));
74690b17f1bSmrg
74790b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc94),
74890b17f1bSmrg	((control->flags & XVMC_CONCEALMENT_MOTION_VECTORS) ? 1 : 0) |
74990b17f1bSmrg	((control->flags & XVMC_Q_SCALE_TYPE) ? 2 : 0) |
75090b17f1bSmrg	((control->intra_dc_precision & 3) << 2) |
75190b17f1bSmrg	(((1 + 0x100000 / mb_width) & 0xfffff) << 4) |
75290b17f1bSmrg	((control->flags & XVMC_INTRA_VLC_FORMAT) ? (1 << 24) : 0));
75390b17f1bSmrg
75490b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc98),
75590b17f1bSmrg	(((control->FHMV_range) & 0xf) << 0) |
75690b17f1bSmrg	(((control->FVMV_range) & 0xf) << 4) |
75790b17f1bSmrg	(((control->BHMV_range) & 0xf) << 8) |
75890b17f1bSmrg	(((control->BVMV_range) & 0xf) << 12) |
75990b17f1bSmrg	((control->flags & XVMC_SECOND_FIELD) ? (1 << 20) : 0) |
76090b17f1bSmrg	(0x0a6 << 16));
76190b17f1bSmrg
76290b17f1bSmrg}
76390b17f1bSmrg
76490b17f1bSmrgvoid
76590b17f1bSmrgviaMpegReset(void *xlp)
76690b17f1bSmrg{
76790b17f1bSmrg    int i, j;
76890b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
76990b17f1bSmrg
77090b17f1bSmrg    BEGIN_RING_AGP(xl, 100);
77190b17f1bSmrg    WAITFLAGS(xl, LL_MODE_DECODER_IDLE);
77290b17f1bSmrg
77390b17f1bSmrg    for (i = 0; i < 14; i++)
77490b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc08), 0);
77590b17f1bSmrg
77690b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc98), 0x400000);
77790b17f1bSmrg
77890b17f1bSmrg    for (i = 0; i < 6; i++) {
77990b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xc0c), 0x43 | 0x20);
78090b17f1bSmrg	for (j = 0xc10; j < 0xc20; j += 4)
78190b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(j), 0);
78290b17f1bSmrg    }
78390b17f1bSmrg
78490b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc0c), 0xc3 | 0x20);
78590b17f1bSmrg    for (j = 0xc10; j < 0xc20; j += 4)
78690b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(j), 0);
78790b17f1bSmrg
78890b17f1bSmrg}
78990b17f1bSmrg
79090b17f1bSmrgvoid
79190b17f1bSmrgviaMpegWriteSlice(void *xlp, CARD8 * slice, int nBytes, CARD32 sCode)
79290b17f1bSmrg{
79390b17f1bSmrg    int i, n, r;
79490b17f1bSmrg    CARD32 *buf;
79590b17f1bSmrg    int count;
79690b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
79790b17f1bSmrg
79890b17f1bSmrg    if (xl->errors & (LL_DECODER_TIMEDOUT |
79990b17f1bSmrg	    LL_IDCT_FIFO_ERROR | LL_SLICE_FIFO_ERROR | LL_SLICE_FAULT))
80090b17f1bSmrg	return;
80190b17f1bSmrg
80290b17f1bSmrg    n = nBytes >> 2;
80390b17f1bSmrg    if (sCode)
80490b17f1bSmrg	nBytes += 4;
80590b17f1bSmrg    r = nBytes & 3;
80690b17f1bSmrg    buf = (CARD32 *) slice;
80790b17f1bSmrg
80890b17f1bSmrg    if (r)
80990b17f1bSmrg	nBytes += 4 - r;
81090b17f1bSmrg
81190b17f1bSmrg    nBytes += 8;
81290b17f1bSmrg
81390b17f1bSmrg    BEGIN_RING_AGP(xl, 4);
81490b17f1bSmrg    WAITFLAGS(xl, LL_MODE_DECODER_IDLE);
81590b17f1bSmrg
81690b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xc9c), nBytes);
81790b17f1bSmrg
81890b17f1bSmrg    if (sCode)
81990b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xca0), sCode);
82090b17f1bSmrg
82190b17f1bSmrg    i = 0;
82290b17f1bSmrg    count = 0;
82390b17f1bSmrg
82490b17f1bSmrg    do {
82590b17f1bSmrg	count += (LL_AGP_CMDBUF_SIZE - 20) >> 1;
82690b17f1bSmrg	count = (count > n) ? n : count;
82790b17f1bSmrg	BEGIN_RING_AGP(xl, (count - i) << 1);
82890b17f1bSmrg
82990b17f1bSmrg	for (; i < count; i++) {
83090b17f1bSmrg	    OUT_RING_QW_AGP(xl, H1_ADDR(0xca0), *buf++);
83190b17f1bSmrg	}
83290b17f1bSmrg    } while (i < n);
83390b17f1bSmrg
83490b17f1bSmrg    BEGIN_RING_AGP(xl, 6);
83590b17f1bSmrg
83690b17f1bSmrg    if (r) {
83790b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(0xca0), *buf & ((1 << (r << 3)) - 1));
83890b17f1bSmrg    }
83990b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xca0), 0);
84090b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(0xca0), 0);
84190b17f1bSmrg
84290b17f1bSmrg}
84390b17f1bSmrg
84490b17f1bSmrgvoid
84590b17f1bSmrgviaVideoSubPictureOffLocked(void *xlp)
84690b17f1bSmrg{
84790b17f1bSmrg
84890b17f1bSmrg    CARD32 stride;
84990b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
85090b17f1bSmrg
85190b17f1bSmrg    stride = VIDIN(xl, SUBP_CONTROL_STRIDE);
85290b17f1bSmrg
85390b17f1bSmrg    pciCommand(xl, SUBP_CONTROL_STRIDE, stride & ~SUBP_HQV_ENABLE,
85490b17f1bSmrg	LL_MODE_VIDEO);
85590b17f1bSmrg}
85690b17f1bSmrg
85790b17f1bSmrgvoid
85890b17f1bSmrgviaVideoSubPictureLocked(void *xlp, ViaXvMCSubPicture * pViaSubPic)
85990b17f1bSmrg{
86090b17f1bSmrg
86190b17f1bSmrg    unsigned i;
86290b17f1bSmrg    CARD32 cWord;
86390b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
86490b17f1bSmrg
86590b17f1bSmrg    for (i = 0; i < VIA_SUBPIC_PALETTE_SIZE; ++i) {
86690b17f1bSmrg	pciCommand(xl, RAM_TABLE_CONTROL, pViaSubPic->palette[i],
86790b17f1bSmrg	    LL_MODE_VIDEO);
86890b17f1bSmrg    }
86990b17f1bSmrg
87090b17f1bSmrg    pciCommand(xl, SUBP_STARTADDR, pViaSubPic->offset, 0);
87190b17f1bSmrg    cWord = (pViaSubPic->stride & SUBP_STRIDE_MASK) | SUBP_HQV_ENABLE;
87290b17f1bSmrg    cWord |= (pViaSubPic->ia44) ? SUBP_IA44 : SUBP_AI44;
87390b17f1bSmrg    pciCommand(xl, SUBP_CONTROL_STRIDE, cWord, 0);
87490b17f1bSmrg}
87590b17f1bSmrg
87690b17f1bSmrgvoid
87790b17f1bSmrgviaBlit(void *xlp, unsigned bpp, unsigned srcBase,
87890b17f1bSmrg    unsigned srcPitch, unsigned dstBase, unsigned dstPitch,
87990b17f1bSmrg    unsigned w, unsigned h, int xdir, int ydir, unsigned blitMode,
88090b17f1bSmrg    unsigned color)
88190b17f1bSmrg{
88290b17f1bSmrg
88390b17f1bSmrg    CARD32 dwGEMode = 0, srcY = 0, srcX, dstY = 0, dstX;
88490b17f1bSmrg    CARD32 cmd;
88590b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
88690b17f1bSmrg
88790b17f1bSmrg    if (!w || !h)
88890b17f1bSmrg	return;
88990b17f1bSmrg
89090b17f1bSmrg    switch (bpp) {
89190b17f1bSmrg    case 16:
89290b17f1bSmrg	dwGEMode |= VIA_GEM_16bpp;
89390b17f1bSmrg	break;
89490b17f1bSmrg    case 32:
89590b17f1bSmrg	dwGEMode |= VIA_GEM_32bpp;
89690b17f1bSmrg	break;
89790b17f1bSmrg    default:
89890b17f1bSmrg	dwGEMode |= VIA_GEM_8bpp;
89990b17f1bSmrg	break;
90090b17f1bSmrg    }
90190b17f1bSmrg
90290b17f1bSmrg    srcX = srcBase & 31;
90390b17f1bSmrg    dstX = dstBase & 31;
90490b17f1bSmrg    switch (bpp) {
90590b17f1bSmrg    case 16:
90690b17f1bSmrg	dwGEMode |= VIA_GEM_16bpp;
90790b17f1bSmrg	srcX >>= 2;
90890b17f1bSmrg	dstX >>= 2;
90990b17f1bSmrg	break;
91090b17f1bSmrg    case 32:
91190b17f1bSmrg	dwGEMode |= VIA_GEM_32bpp;
91290b17f1bSmrg	srcX >>= 4;
91390b17f1bSmrg	dstX >>= 4;
91490b17f1bSmrg	break;
91590b17f1bSmrg    default:
91690b17f1bSmrg	dwGEMode |= VIA_GEM_8bpp;
91790b17f1bSmrg	break;
91890b17f1bSmrg    }
91990b17f1bSmrg
92090b17f1bSmrg    BEGIN_RING_AGP(xl, 20);
92190b17f1bSmrg    WAITFLAGS(xl, LL_MODE_2D);
92290b17f1bSmrg
92390b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_GEMODE), dwGEMode);
92490b17f1bSmrg    cmd = 0;
92590b17f1bSmrg
92690b17f1bSmrg    if (xdir < 0) {
92790b17f1bSmrg	cmd |= VIA_GEC_DECX;
92890b17f1bSmrg	srcX += (w - 1);
92990b17f1bSmrg	dstX += (w - 1);
93090b17f1bSmrg    }
93190b17f1bSmrg    if (ydir < 0) {
93290b17f1bSmrg	cmd |= VIA_GEC_DECY;
93390b17f1bSmrg	srcY += (h - 1);
93490b17f1bSmrg	dstY += (h - 1);
93590b17f1bSmrg    }
93690b17f1bSmrg
93790b17f1bSmrg    switch (blitMode) {
93890b17f1bSmrg    case VIABLIT_TRANSCOPY:
93990b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_SRCCOLORKEY), color);
94090b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_KEYCONTROL), 0x4000);
94190b17f1bSmrg	cmd |= VIA_GEC_BLT | (VIA_BLIT_COPY << 24);
94290b17f1bSmrg	break;
94390b17f1bSmrg    case VIABLIT_FILL:
94490b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_FGCOLOR), color);
94590b17f1bSmrg	cmd |= VIA_GEC_BLT | VIA_GEC_FIXCOLOR_PAT | (VIA_BLIT_FILL << 24);
94690b17f1bSmrg	break;
94790b17f1bSmrg    default:
94890b17f1bSmrg	OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_KEYCONTROL), 0x0);
94990b17f1bSmrg	cmd |= VIA_GEC_BLT | (VIA_BLIT_COPY << 24);
95090b17f1bSmrg    }
95190b17f1bSmrg
95290b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_SRCBASE), (srcBase & ~31) >> 3);
95390b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_DSTBASE), (dstBase & ~31) >> 3);
95490b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_PITCH), VIA_PITCH_ENABLE |
95590b17f1bSmrg	(srcPitch >> 3) | (((dstPitch) >> 3) << 16));
95690b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_SRCPOS), ((srcY << 16) | srcX));
95790b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_DSTPOS), ((dstY << 16) | dstX));
95890b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_DIMENSION),
95990b17f1bSmrg	(((h - 1) << 16) | (w - 1)));
96090b17f1bSmrg    OUT_RING_QW_AGP(xl, H1_ADDR(VIA_REG_GECMD), cmd);
96190b17f1bSmrg}
96290b17f1bSmrg
96390b17f1bSmrgunsigned
96490b17f1bSmrgsyncXvMCLowLevel(void *xlp, unsigned int mode, unsigned int doSleep,
96590b17f1bSmrg    CARD32 timeStamp)
96690b17f1bSmrg{
96790b17f1bSmrg    unsigned errors;
96890b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
96990b17f1bSmrg
97090b17f1bSmrg    if (mode == 0) {
97190b17f1bSmrg	errors = xl->errors;
97290b17f1bSmrg	xl->errors = 0;
97390b17f1bSmrg	return errors;
97490b17f1bSmrg    }
97590b17f1bSmrg
97690b17f1bSmrg    if ((mode & (LL_MODE_VIDEO | LL_MODE_3D)) || !xl->use_agp) {
97790b17f1bSmrg	if (xl->performLocking)
97890b17f1bSmrg	    hwlLock(xl, 0);
97990b17f1bSmrg	if ((mode != LL_MODE_VIDEO))
98090b17f1bSmrg	    syncDMA(xl, doSleep);
98190b17f1bSmrg	if (mode & LL_MODE_3D)
98290b17f1bSmrg	    syncAccel(xl, mode, doSleep);
98390b17f1bSmrg	if (mode & LL_MODE_VIDEO)
98490b17f1bSmrg	    syncVideo(xl, doSleep);
98590b17f1bSmrg	if (xl->performLocking)
98690b17f1bSmrg	    hwlUnlock(xl, 0);
98790b17f1bSmrg    } else {
98890b17f1bSmrg	viaDMAWaitTimeStamp(xl, timeStamp, doSleep);
98990b17f1bSmrg    }
99090b17f1bSmrg
99190b17f1bSmrg    if (mode & (LL_MODE_DECODER_SLICE | LL_MODE_DECODER_IDLE))
99290b17f1bSmrg	syncMpeg(xl, mode, doSleep);
99390b17f1bSmrg
99490b17f1bSmrg    errors = xl->errors;
99590b17f1bSmrg    xl->errors = 0;
99690b17f1bSmrg
99790b17f1bSmrg    return errors;
99890b17f1bSmrg}
99990b17f1bSmrg
100090b17f1bSmrgextern void *
100190b17f1bSmrginitXvMCLowLevel(int fd, drm_context_t * ctx,
100290b17f1bSmrg    drmLockPtr hwLock, drmAddress mmioAddress,
100390b17f1bSmrg    drmAddress fbAddress, unsigned fbStride, unsigned fbDepth,
100490b17f1bSmrg    unsigned width, unsigned height, int useAgp, unsigned chipId)
100590b17f1bSmrg{
100690b17f1bSmrg    int ret;
100790b17f1bSmrg    XvMCLowLevel *xl;
100890b17f1bSmrg
100990b17f1bSmrg    if (chipId == PCI_CHIP_VT3259 || chipId == PCI_CHIP_VT3364) {
101090b17f1bSmrg	fprintf(stderr, "You are using an XvMC driver for the wrong chip.\n");
101190b17f1bSmrg	fprintf(stderr, "Chipid is 0x%04x.\n", chipId);
101290b17f1bSmrg	return NULL;
101390b17f1bSmrg    }
101490b17f1bSmrg
101590b17f1bSmrg    xl = (XvMCLowLevel *) malloc(sizeof(XvMCLowLevel));
101690b17f1bSmrg
101790b17f1bSmrg    if (!xl)
101890b17f1bSmrg	return NULL;
101990b17f1bSmrg
102090b17f1bSmrg    xl->agp_pos = 0;
102190b17f1bSmrg    xl->pci_pos = 0;
102290b17f1bSmrg    xl->use_agp = useAgp;
102390b17f1bSmrg    xl->fd = fd;
102490b17f1bSmrg    xl->drmcontext = ctx;
102590b17f1bSmrg    xl->hwLock = hwLock;
102690b17f1bSmrg    xl->mmioAddress = mmioAddress;
102790b17f1bSmrg    xl->fbAddress = fbAddress;
102890b17f1bSmrg    xl->curWaitFlags = 0;
102990b17f1bSmrg    xl->performLocking = 1;
103090b17f1bSmrg    xl->errors = 0;
103190b17f1bSmrg    xl->agpSync = 0;
103290b17f1bSmrg    ret = viaDMAInitTimeStamp(xl);
103390b17f1bSmrg    if (ret) {
103490b17f1bSmrg	free(xl);
103590b17f1bSmrg	return NULL;
103690b17f1bSmrg    }
103790b17f1bSmrg    return xl;
103890b17f1bSmrg}
103990b17f1bSmrg
104090b17f1bSmrgvoid
104190b17f1bSmrgsetLowLevelLocking(void *xlp, int performLocking)
104290b17f1bSmrg{
104390b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
104490b17f1bSmrg
104590b17f1bSmrg    xl->performLocking = performLocking;
104690b17f1bSmrg}
104790b17f1bSmrg
104890b17f1bSmrgvoid
104990b17f1bSmrgcloseXvMCLowLevel(void *xlp)
105090b17f1bSmrg{
105190b17f1bSmrg    XvMCLowLevel *xl = (XvMCLowLevel *) xlp;
105290b17f1bSmrg
105390b17f1bSmrg    viaDMACleanupTimeStamp(xl);
105490b17f1bSmrg    free(xl);
105590b17f1bSmrg}
1056