1/*
2 * Copyright 1992-2003 by Alan Hourihane, North Wales, UK.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Alan Hourihane not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Alan Hourihane makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as is" without express or implied warranty.
13 *
14 * ALAN HOURIHANE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL ALAN HOURIHANE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 *
22 * Author:  Alan Hourihane, alanh@fairlite.demon.co.uk
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "xf86.h"
30#include "xf86_OSproc.h"
31#include "compiler.h"
32#include "xf86Pci.h"
33#include "xf86fbman.h"
34#include "regionstr.h"
35
36#include "trident.h"
37#include "trident_regs.h"
38#include <X11/extensions/Xv.h>
39#include "dixstruct.h"
40#include "fourcc.h"
41
42#define OFF_DELAY 	800  /* milliseconds */
43#define FREE_DELAY 	60000
44
45#define OFF_TIMER 	0x01
46#define FREE_TIMER	0x02
47#define CLIENT_VIDEO_ON	0x04
48
49#define TIMER_MASK      (OFF_TIMER | FREE_TIMER)
50
51static XF86VideoAdaptorPtr TRIDENTSetupImageVideo(ScreenPtr);
52static void TRIDENTInitOffscreenImages(ScreenPtr);
53static void TRIDENTStopVideo(ScrnInfoPtr, pointer, Bool);
54static int TRIDENTSetPortAttribute(ScrnInfoPtr, Atom, INT32, pointer);
55static int TRIDENTGetPortAttribute(ScrnInfoPtr, Atom ,INT32 *, pointer);
56static void TRIDENTQueryBestSize(ScrnInfoPtr, Bool,
57	short, short, short, short, unsigned int *, unsigned int *, pointer);
58static int TRIDENTPutImage( ScrnInfoPtr,
59	short, short, short, short, short, short, short, short,
60	int, unsigned char*, short, short, Bool, RegionPtr, pointer,
61	DrawablePtr);
62static int TRIDENTQueryImageAttributes(ScrnInfoPtr,
63	int, unsigned short *, unsigned short *,  int *, int *);
64static void TRIDENTVideoTimerCallback(ScrnInfoPtr pScrn, Time time);
65static void tridentSetVideoContrast(TRIDENTPtr pTrident,int value);
66static void tridentSetVideoParameters(TRIDENTPtr pTrident, int brightness,
67				      int saturation, int hue);
68void tridentFixFrame(ScrnInfoPtr pScrn, int *fixFrame);
69static void WaitForVBlank(ScrnInfoPtr pScrn);
70
71#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
72
73static Atom xvColorKey, xvSaturation, xvBrightness, xvHUE,  xvContrast;
74
75void TRIDENTInitVideo(ScreenPtr pScreen)
76{
77    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
78    XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
79    XF86VideoAdaptorPtr newAdaptor = NULL;
80    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
81    int num_adaptors;
82
83    /*
84     * The following has been tested on:
85     *
86     * 9525         : flags: None
87     * Image985     : flags: None
88     * Cyber9397(DVD) : flags: VID_ZOOM_NOMINI
89     * CyberBlade/i7: flags: VID_ZOOM_INV | VID_ZOOM_MINI
90     * CyberBlade/i1: flags: VID_ZOOM_INV | VID_ZOOM_MINI
91     * CyberBlade/Ai1: flags: VID_ZOOM_INV
92     * Cyber 9540   : flags: VID_ZOOM_INV | VID_SHIFT_4
93     * CyberXPm8    : flags: VID_ZOOM_INV | VID_SHIFT_4
94     *
95     * When you make changes make sure not to break these
96     * Add new chipsets to this list.
97     */
98    if (pTrident->Chipset >= BLADE3D) {
99       pTrident->videoFlags = VID_ZOOM_INV ;
100       if (pTrident->Chipset <= CYBERBLADEI1D)
101	 pTrident->videoFlags |= VID_ZOOM_MINI;
102       else if (pTrident->Chipset < CYBERBLADEAI1 /* verified EE */
103		|| pTrident->Chipset > CYBERBLADEAI1D)
104	 pTrident->videoFlags |= VID_OFF_SHIFT_4;
105    }
106    if (pTrident->Chipset == CYBER9397 || pTrident->Chipset == CYBER9397DVD)
107	pTrident->videoFlags = VID_ZOOM_NOMINI;
108
109    if (pTrident->Chipset == CYBER9397DVD ||
110	pTrident->Chipset == CYBER9525DVD ||
111	(pTrident->Chipset >= BLADE3D && pTrident->Chipset < CYBERBLADEXP4))
112		pTrident->videoFlags |= VID_DOUBLE_LINEBUFFER_FOR_WIDE_SRC;
113
114    newAdaptor = TRIDENTSetupImageVideo(pScreen);
115    TRIDENTInitOffscreenImages(pScreen);
116
117    num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);
118
119    if(newAdaptor) {
120	if(!num_adaptors) {
121	    num_adaptors = 1;
122	    adaptors = &newAdaptor;
123	} else {
124	    newAdaptors =  /* need to free this someplace */
125		malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*));
126	    if(newAdaptors) {
127		memcpy(newAdaptors, adaptors, num_adaptors *
128					sizeof(XF86VideoAdaptorPtr));
129		newAdaptors[num_adaptors] = newAdaptor;
130		adaptors = newAdaptors;
131		num_adaptors++;
132	    }
133	}
134    }
135
136    if(num_adaptors)
137        xf86XVScreenInit(pScreen, adaptors, num_adaptors);
138
139    if(newAdaptors)
140	free(newAdaptors);
141
142    if (pTrident->videoFlags)
143	xf86DrvMsgVerb(pScrn->scrnIndex,X_INFO,3,
144		       "Trident Video Flags: %s %s %s %s\n",
145		   pTrident->videoFlags & VID_ZOOM_INV ? "VID_ZOOM_INV" : "",
146		   pTrident->videoFlags & VID_ZOOM_MINI ? "VID_ZOOM_MINI" : "",                   pTrident->videoFlags & VID_OFF_SHIFT_4 ? "VID_OFF_SHIFT_4"
147		   : "",
148		   pTrident->videoFlags & VID_ZOOM_NOMINI ? "VID_ZOOM_NOMINI"
149		   : "");
150
151}
152
153/* client libraries expect an encoding */
154static XF86VideoEncodingRec DummyEncoding[1] =
155{
156 {
157   0,
158   "XV_IMAGE",
159   1024, 1024,
160   {1, 1}
161 }
162};
163
164#define NUM_FORMATS 4
165
166static XF86VideoFormatRec Formats[NUM_FORMATS] =
167{
168  {8, PseudoColor},  {15, TrueColor}, {16, TrueColor}, {24, TrueColor}
169};
170
171#define NUM_ATTRIBUTES 5
172
173static XF86AttributeRec Attributes[NUM_ATTRIBUTES] =
174{
175    {XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"},
176    {XvSettable | XvGettable, 0, 187,           "XV_SATURATION"},
177    {XvSettable | XvGettable, 0, 0x3F,          "XV_BRIGHTNESS"},
178    {XvSettable | XvGettable, 0, 360 ,          "XV_HUE"},
179    {XvSettable | XvGettable, 0, 7,           "XV_CONTRAST"}
180};
181
182#define NUM_IMAGES 3
183
184static XF86ImageRec Images[NUM_IMAGES] =
185{
186   {
187	0x36315652,
188        XvRGB,
189	LSBFirst,
190	{'R','V','1','6',
191	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
192	16,
193	XvPacked,
194	1,
195	16, 0xF800, 0x07E0, 0x001F,
196	0, 0, 0,
197	0, 0, 0,
198	0, 0, 0,
199	{'R','V','B',0,
200	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
201	XvTopToBottom
202   },
203   XVIMAGE_YV12,
204   XVIMAGE_YUY2
205};
206
207typedef struct {
208   FBLinearPtr	linear;
209   RegionRec	clip;
210   CARD32	colorKey;
211   CARD8        Saturation;
212   CARD8        Brightness;
213   CARD16       HUE;
214   INT8         Contrast;
215   CARD32	videoStatus;
216   Time		offTime;
217   Time		freeTime;
218   int          fixFrame;
219} TRIDENTPortPrivRec, *TRIDENTPortPrivPtr;
220
221
222#define GET_PORT_PRIVATE(pScrn) \
223   (TRIDENTPortPrivPtr)((TRIDENTPTR(pScrn))->adaptor->pPortPrivates[0].ptr)
224
225void TRIDENTResetVideo(ScrnInfoPtr pScrn)
226{
227    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
228    TRIDENTPortPrivPtr pPriv = pTrident->adaptor->pPortPrivates[0].ptr;
229    int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
230    int red, green, blue;
231    int tmp;
232
233    WaitForVBlank(pScrn);
234    OUTW(vgaIOBase + 4, 0x848E);
235
236    if (pTrident->Chipset >= CYBER9388) {
237    	OUTW(vgaIOBase + 4, 0x80B9);
238    	OUTW(vgaIOBase + 4, 0x00BE);
239    	OUTW(0x3C4, 0xC057);
240    	OUTW(0x3C4, 0x3420);
241    	OUTW(0x3C4, 0x3037);
242    } else {
243	if (pTrident->Chipset >= PROVIDIA9682) {
244    	    OUTB(0x83C8, 0x57);
245    	    OUTB(0x83C6, 0xC0);
246    	    OUTW(vgaIOBase + 4, 0x26BE);
247	} else {
248    	    OUTB(0x83C8, 0x37);
249    	    OUTB(0x83C6, 0x01);
250    	    OUTB(0x83C8, 0x00);
251    	    OUTB(0x83C6, 0x00);
252	}
253    }
254
255    if (pTrident->Chipset >= BLADEXP) {
256	OUTW(0x3C4, 0x007A);
257	OUTW(0x3C4, 0x007D);
258    }
259    if (pTrident->Chipset == CYBERBLADEXP4) {
260    	OUTW(0x3CE, 0x0462);
261    }
262    switch (pScrn->depth) {
263    case 8:
264	VIDEOOUT(pPriv->colorKey, pTrident->keyOffset);
265	VIDEOOUT(0x00, (pTrident->keyOffset + 1));
266	VIDEOOUT(0x00, (pTrident->keyOffset + 2));
267	VIDEOOUT(0xFF, (pTrident->keyOffset + 4));
268	VIDEOOUT(0x00, (pTrident->keyOffset + 5));
269	VIDEOOUT(0x00, (pTrident->keyOffset + 6));
270	break;
271    default:
272	red = (pPriv->colorKey & pScrn->mask.red) >> pScrn->offset.red;
273	green = (pPriv->colorKey & pScrn->mask.green) >> pScrn->offset.green;
274	blue = (pPriv->colorKey & pScrn->mask.blue) >> pScrn->offset.blue;
275	switch (pScrn->depth) {
276	case 15:
277	    tmp = (red << 10) | (green << 5) | (blue);
278	    VIDEOOUT((tmp & 0xff), pTrident->keyOffset);
279	    VIDEOOUT((tmp & 0xff00)>>8, (pTrident->keyOffset + 1));
280	    VIDEOOUT(0x00, (pTrident->keyOffset + 2));
281	    VIDEOOUT(0xFF, (pTrident->keyOffset + 4));
282	    VIDEOOUT(0xFF, (pTrident->keyOffset + 5));
283	    VIDEOOUT(0x00, (pTrident->keyOffset + 6));
284	    break;
285	case 16:
286	    tmp = (red << 11) | (green << 5) | (blue);
287	    VIDEOOUT((tmp & 0xff), pTrident->keyOffset);
288	    VIDEOOUT((tmp & 0xff00)>>8, (pTrident->keyOffset + 1));
289	    VIDEOOUT(0x00, (pTrident->keyOffset + 2));
290	    VIDEOOUT(0xFF, (pTrident->keyOffset + 4));
291	    VIDEOOUT(0xFF, (pTrident->keyOffset + 5));
292	    VIDEOOUT(0x00, (pTrident->keyOffset + 6));
293	    break;
294	case 24:
295	    VIDEOOUT(blue, pTrident->keyOffset);
296	    VIDEOOUT(green, (pTrident->keyOffset + 1));
297	    VIDEOOUT(red, (pTrident->keyOffset + 2));
298	    VIDEOOUT(0xFF, (pTrident->keyOffset + 4));
299	    VIDEOOUT(0xFF, (pTrident->keyOffset + 5));
300	    VIDEOOUT(0xFF, (pTrident->keyOffset + 6));
301	    break;
302	}
303    }
304
305    if (pTrident->Chipset >= CYBER9388) {
306    	tridentSetVideoContrast(pTrident,pPriv->Contrast);
307    	tridentSetVideoParameters(pTrident,pPriv->Brightness,pPriv->Saturation,
308                            pPriv->HUE);
309    }
310}
311
312
313static XF86VideoAdaptorPtr
314TRIDENTSetupImageVideo(ScreenPtr pScreen)
315{
316    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
317    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
318    XF86VideoAdaptorPtr adapt;
319    TRIDENTPortPrivPtr pPriv;
320
321    if(!(adapt = calloc(1, sizeof(XF86VideoAdaptorRec) +
322			    sizeof(TRIDENTPortPrivRec) +
323			    sizeof(DevUnion))))
324	return NULL;
325
326    adapt->type = XvWindowMask | XvInputMask | XvImageMask;
327    adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
328    adapt->name = "Trident Backend Scaler";
329    adapt->nEncodings = 1;
330    adapt->pEncodings = DummyEncoding;
331    adapt->nFormats = NUM_FORMATS;
332    adapt->pFormats = Formats;
333    adapt->nPorts = 1;
334    adapt->pPortPrivates = (DevUnion*)(&adapt[1]);
335    pPriv = (TRIDENTPortPrivPtr)(&adapt->pPortPrivates[1]);
336    adapt->pPortPrivates[0].ptr = (pointer)(pPriv);
337    adapt->pAttributes = Attributes;
338    adapt->nImages = NUM_IMAGES;
339    if (pTrident->Chipset >= CYBER9388) {
340    	adapt->nAttributes = NUM_ATTRIBUTES;
341    } else {
342    	adapt->nAttributes = 1; /* Just colorkey */
343    }
344    adapt->pImages = Images;
345    adapt->PutVideo = NULL;
346    adapt->PutStill = NULL;
347    adapt->GetVideo = NULL;
348    adapt->GetStill = NULL;
349    adapt->StopVideo = TRIDENTStopVideo;
350    adapt->SetPortAttribute = TRIDENTSetPortAttribute;
351    adapt->GetPortAttribute = TRIDENTGetPortAttribute;
352    adapt->QueryBestSize = TRIDENTQueryBestSize;
353    adapt->PutImage = TRIDENTPutImage;
354    adapt->QueryImageAttributes = TRIDENTQueryImageAttributes;
355
356    pPriv->colorKey = pTrident->videoKey & ((1 << pScrn->depth) - 1);
357    pPriv->Brightness = 45;
358    pPriv->Saturation = 80;
359    pPriv->Contrast = 4;
360    pPriv->HUE = 0;
361    pPriv->videoStatus = 0;
362    pPriv->fixFrame = 100;
363
364    /* gotta uninit this someplace */
365    REGION_NULL(pScreen, &pPriv->clip);
366
367    pTrident->adaptor = adapt;
368
369    xvColorKey   = MAKE_ATOM("XV_COLORKEY");
370
371    if (pTrident->Chipset >= CYBER9388) {
372    	xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
373    	xvSaturation = MAKE_ATOM("XV_SATURATION");
374    	xvHUE        = MAKE_ATOM("XV_HUE");
375    	xvContrast   = MAKE_ATOM("XV_CONTRAST");
376    }
377
378    if (pTrident->Chipset >= PROVIDIA9682)
379	pTrident->keyOffset = 0x50;
380    else
381	pTrident->keyOffset = 0x30;
382
383    TRIDENTResetVideo(pScrn);
384
385    return adapt;
386}
387
388
389static void
390TRIDENTStopVideo(ScrnInfoPtr pScrn, pointer data, Bool shutdown)
391{
392  TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
393  TRIDENTPortPrivPtr pPriv = (TRIDENTPortPrivPtr)data;
394  int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
395
396  REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
397
398  if(shutdown) {
399     if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
400    	WaitForVBlank(pScrn);
401	OUTW(vgaIOBase + 4, 0x848E);
402	OUTW(vgaIOBase + 4, 0x0091);
403     }
404     if(pPriv->linear) {
405	xf86FreeOffscreenLinear(pPriv->linear);
406	pPriv->linear = NULL;
407     }
408     pPriv->videoStatus = 0;
409  } else {
410     if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
411	pPriv->videoStatus |= OFF_TIMER;
412	pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
413	pTrident->VideoTimerCallback = TRIDENTVideoTimerCallback;
414     }
415  }
416}
417
418#undef PI
419#define PI 3.14159265
420
421static void
422tridentSetVideoContrast(TRIDENTPtr pTrident,int value)
423{
424  OUTW(0x3C4, (((value & 0x7)|((value & 0x7) << 4)) << 8) | 0xBC);
425}
426
427static void
428tridentSetVideoParameters(TRIDENTPtr pTrident, int brightness,
429			  int saturation, int hue)
430{
431    double dtmp;
432    CARD8 sign, tmp, tmp1;
433
434    if (brightness >= 0x20)
435      brightness -= 0x20;
436    else
437      brightness += 0x20;
438    dtmp = sin((double)hue / 180.0 * PI) * saturation / 12.5;
439    sign = (dtmp < 0) ? 1 << 1 : 0;
440    tmp1 = ((int)fabs(dtmp) >> 4) & 0x1;
441    tmp = brightness << 2 | sign | tmp1;
442    OUTW(0x3C4, tmp << 8 | 0xB1);
443
444    tmp1 = ((int)fabs(dtmp) & 0x7 ) << 5;
445    dtmp = cos((double)hue / 180.0 * PI) * saturation / 12.5;
446    sign = (dtmp < 0) ? 1 << 4 : 0;
447    tmp1 |= (int)fabs(dtmp)  & 0xf;
448    tmp = sign | tmp1;
449    OUTW(0x3C4, tmp << 8 | 0xB0);
450}
451
452static int
453TRIDENTSetPortAttribute(
454  ScrnInfoPtr pScrn,
455  Atom attribute,
456  INT32 value,
457  pointer data
458){
459  TRIDENTPortPrivPtr pPriv = (TRIDENTPortPrivPtr)data;
460  TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
461
462  if(attribute == xvColorKey) {
463	int red, green, blue;
464	int tmp;
465	pPriv->colorKey = value;
466	switch (pScrn->depth) {
467	case 8:
468	    VIDEOOUT(pPriv->colorKey, pTrident->keyOffset);
469	    VIDEOOUT(0x00, (pTrident->keyOffset + 1));
470	    VIDEOOUT(0x00, (pTrident->keyOffset + 2));
471	    break;
472	default:
473	    red = (pPriv->colorKey & pScrn->mask.red) >> pScrn->offset.red;
474	    green = (pPriv->colorKey & pScrn->mask.green) >> pScrn->offset.green;
475	    blue = (pPriv->colorKey & pScrn->mask.blue) >> pScrn->offset.blue;
476	    switch (pScrn->depth) {
477	    case 15:
478	    	tmp = (red << 10) | (green << 5) | (blue);
479	    	VIDEOOUT((tmp&0xff), pTrident->keyOffset);
480	    	VIDEOOUT((tmp&0xff00)>>8, (pTrident->keyOffset + 1));
481	    	VIDEOOUT(0x00, (pTrident->keyOffset + 2));
482		break;
483	    case 16:
484	    	tmp = (red << 11) | (green << 5) | (blue);
485	    	VIDEOOUT((tmp&0xff), pTrident->keyOffset);
486	    	VIDEOOUT((tmp&0xff00)>>8, (pTrident->keyOffset + 1));
487	    	VIDEOOUT(0x00, (pTrident->keyOffset + 2));
488		break;
489	    case 24:
490	    	VIDEOOUT(blue, pTrident->keyOffset);
491	    	VIDEOOUT(green, (pTrident->keyOffset + 1));
492	    	VIDEOOUT(red, (pTrident->keyOffset + 2));
493		break;
494	    }
495	}
496	REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
497  } else if (attribute == xvBrightness) {
498    if ((value < 0) || (value > 0x3f))
499      return BadValue;
500    pPriv->Brightness = value;
501    tridentSetVideoParameters(pTrident, pPriv->Brightness, pPriv->Saturation,
502			      pPriv->HUE);
503  } else if (attribute == xvSaturation) {
504    if ((value < 0) || (value > 187))
505      return BadValue;
506    pPriv->Saturation = value;
507    tridentSetVideoParameters(pTrident, pPriv->Brightness, pPriv->Saturation,
508			      pPriv->HUE);
509  } else if (attribute == xvHUE) {
510    if ((value < 0) || (value > 360))
511      return BadValue;
512    pPriv->HUE = value;
513    tridentSetVideoParameters(pTrident, pPriv->Brightness, pPriv->Saturation,
514			      pPriv->HUE);
515  } else if (attribute == xvContrast) {
516    if ((value < 0) || (value > 7))
517      return BadValue;
518    pPriv->Contrast = value;
519    tridentSetVideoContrast(pTrident,value);
520  } else
521    return BadMatch;
522
523  return Success;
524}
525
526static int
527TRIDENTGetPortAttribute(
528  ScrnInfoPtr pScrn,
529  Atom attribute,
530  INT32 *value,
531  pointer data
532){
533  TRIDENTPortPrivPtr pPriv = (TRIDENTPortPrivPtr)data;
534
535  if(attribute == xvColorKey) {
536	*value = pPriv->colorKey;
537  } else if(attribute == xvBrightness) {
538	*value = pPriv->Brightness;
539  } else if(attribute == xvSaturation) {
540	*value = pPriv->Saturation;
541  } else if (attribute == xvHUE) {
542	*value = pPriv->HUE;
543  } else if (attribute == xvContrast) {
544	*value = pPriv->Contrast;
545  } else
546    return BadMatch;
547
548  return Success;
549}
550
551static void
552TRIDENTQueryBestSize(
553  ScrnInfoPtr pScrn,
554  Bool motion,
555  short vid_w, short vid_h,
556  short drw_w, short drw_h,
557  unsigned int *p_w, unsigned int *p_h,
558  pointer data
559){
560  *p_w = drw_w;
561  *p_h = drw_h;
562
563  if(*p_w > 16384) *p_w = 16384;
564}
565
566
567static FBLinearPtr
568TRIDENTAllocateMemory(
569   ScrnInfoPtr pScrn,
570   FBLinearPtr linear,
571   int size
572){
573   ScreenPtr pScreen;
574   FBLinearPtr new_linear;
575
576   if(linear) {
577	if(linear->size >= size)
578	   return linear;
579
580        if(xf86ResizeOffscreenLinear(linear, size))
581	   return linear;
582
583	xf86FreeOffscreenLinear(linear);
584   }
585
586   pScreen = xf86ScrnToScreen(pScrn);
587
588   new_linear = xf86AllocateOffscreenLinear(pScreen, size, 16,
589   						NULL, NULL, NULL);
590
591   if(!new_linear) {
592	int max_size;
593
594	xf86QueryLargestOffscreenLinear(pScreen, &max_size, 16,
595						PRIORITY_EXTREME);
596
597	if(max_size < size)
598	   return NULL;
599
600	xf86PurgeUnlockedOffscreenAreas(pScreen);
601	new_linear = xf86AllocateOffscreenLinear(pScreen, size, 16,
602						NULL, NULL, NULL);
603   }
604
605   return new_linear;
606}
607
608static void
609TRIDENTDisplayVideo(
610    ScrnInfoPtr pScrn,
611    int id,
612    int offset,
613    short width, short height,
614    int pitch,
615    int x1, int y1, int x2, int y2,
616    BoxPtr dstBox,
617    short src_w, short src_h,
618    short drw_w, short drw_h
619){
620    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
621    int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
622    int zoomx1, zoomx2, zoomy1, zoomy2;
623    int tx1,tx2;
624    int ty1,ty2;
625
626    switch(id) {
627    case 0x35315652:		/* RGB15 */
628    case 0x36315652:		/* RGB16 */
629	if (pTrident->Chipset >= CYBER9388) {
630    	    OUTW(vgaIOBase + 4, 0x22BF);
631	    OUTW(vgaIOBase + 4, 0x248F);
632	} else {
633    	    OUTW(vgaIOBase + 4, 0x118F);
634	}
635	break;
636    case FOURCC_YV12:		/* YV12 */
637    case FOURCC_YUY2:		/* YUY2 */
638    default:
639	if (pTrident->Chipset >= CYBER9388) {
640    	    OUTW(vgaIOBase + 4, 0x00BF);
641	    OUTW(vgaIOBase + 4, 0x208F);
642	} else {
643    	    OUTW(vgaIOBase + 4, 0x108F);
644	}
645	break;
646    }
647    tx1 = dstBox->x1 + pTrident->hsync;
648    tx2 = dstBox->x2 + pTrident->hsync + pTrident->hsync_rskew;
649    ty1 = dstBox->y1 + pTrident->vsync - 2;
650    ty2 = dstBox->y2 + pTrident->vsync + 2 + pTrident->vsync_bskew;
651
652    OUTW(vgaIOBase + 4, (tx1 & 0xff) <<8 | 0x86);
653    OUTW(vgaIOBase + 4, (tx1 & 0xff00)   | 0x87);
654    OUTW(vgaIOBase + 4, (ty1 & 0xff) <<8 | 0x88);
655    OUTW(vgaIOBase + 4, (ty1 & 0xff00)   | 0x89);
656    OUTW(vgaIOBase + 4, (tx2 & 0xff) <<8 | 0x8A);
657    OUTW(vgaIOBase + 4, (tx2 & 0xff00)   | 0x8B);
658    OUTW(vgaIOBase + 4, (ty2 & 0xff) <<8 | 0x8C);
659    OUTW(vgaIOBase + 4, (ty2 & 0xff00)   | 0x8D);
660
661    offset += (x1 >> 15) & ~0x01;
662
663    if (pTrident->videoFlags & VID_OFF_SHIFT_4)
664        offset = offset >> 4;
665    else
666        offset = offset >> 3;
667
668    OUTW(vgaIOBase + 4, (((width<<1) & 0xff)<<8)   | 0x90);
669    OUTW(vgaIOBase + 4, ((width<<1) & 0xff00)      | 0x91);
670    OUTW(vgaIOBase + 4, ((offset) & 0xff) << 8     | 0x92);
671    OUTW(vgaIOBase + 4, ((offset) & 0xff00)        | 0x93);
672    if (pTrident->Chipset >= CYBER9397) {
673    	OUTW(vgaIOBase + 4, ((offset) & 0x0f0000) >> 8 | 0x94);
674    } else {
675    	OUTW(vgaIOBase + 4, ((offset) & 0x070000) >> 8 | 0x94);
676    }
677
678    /* Horizontal Zoom */
679    if (pTrident->videoFlags & VID_ZOOM_INV) {
680	if ((pTrident->videoFlags & VID_ZOOM_MINI) && src_w > drw_w)
681	    zoomx2 = (int)((float)drw_w/(float)src_w * 1024)
682		| (((int)((float)src_w/(float)drw_w) - 1)&7)<<10 | 0x8000;
683	else
684	    zoomx2 = (int)(float)src_w/(float)drw_w * 1024;
685
686	OUTW(vgaIOBase + 4, (zoomx2&0xff)<<8 | 0x80);
687	OUTW(vgaIOBase + 4, (zoomx2&0x9f00) | 0x81);
688    } else {
689	if (drw_w == src_w
690	    || ((pTrident->videoFlags & VID_ZOOM_NOMINI) && (src_w > drw_w))) {
691	    OUTW(vgaIOBase + 4, 0x0080);
692	    OUTW(vgaIOBase + 4, 0x0081);
693	} else
694	    if (drw_w > src_w) {
695		float z;
696
697		z = (float)((drw_w)/(float)src_w) - 1.0;
698
699		zoomx1 =  z;
700		zoomx2 = (z - (int)zoomx1 ) * 1024;
701
702		OUTW(vgaIOBase + 4, (zoomx2&0xff)<<8 | 0x80);
703		OUTW(vgaIOBase + 4, (zoomx1&0x0f)<<10 | (zoomx2&0x0300) |0x81);
704	    } else {
705		zoomx1 =   ((float)drw_w/(float)src_w);
706		zoomx2 = ( ((float)drw_w/(float)src_w) - (int)zoomx1 ) * 1024;
707		OUTW(vgaIOBase + 4, (zoomx2&0xff)<<8 |   0x80);
708		OUTW(vgaIOBase + 4, (zoomx2&0x0300)|
709		     (((int)((float)src_w/(float)drw_w)-1)&7)<<10 | 0x8081);
710	    }
711    }
712
713    /* Vertical Zoom */
714    if (pTrident->videoFlags & VID_ZOOM_INV) {
715	if ((pTrident->videoFlags & VID_ZOOM_MINI) && src_h > drw_h)
716	    zoomy2 = (int)(( ((float)drw_h/(float)src_h)) * 1024)
717		| (((int)((float)src_h/(float)drw_h)-1)&7)<<10
718		| 0x8000;
719	else
720	    zoomy2 = ( ((float)src_h/(float)drw_h)) * 1024;
721	OUTW(vgaIOBase + 4, (zoomy2&0xff)<<8 | 0x82);
722	OUTW(vgaIOBase + 4, (zoomy2&0x9f00) | 0x0083);
723    } else {
724	if (drw_h == src_h
725	    || ((pTrident->videoFlags & VID_ZOOM_NOMINI) && (src_h > drw_h))) {
726	    OUTW(vgaIOBase + 4, 0x0082);
727	    OUTW(vgaIOBase + 4, 0x0083);
728	} else
729	    if (drw_h > src_h) {
730		float z;
731
732		z = (float)drw_h/(float)src_h - 1;
733		zoomy1 =  z;
734		zoomy2 = (z - (int)zoomy1 ) * 1024;
735
736		OUTW(vgaIOBase + 4, (zoomy2&0xff)<<8 | 0x82);
737		OUTW(vgaIOBase + 4, (zoomy1&0x0f)<<10 | (zoomy2&0x0300) |0x83);
738	    } else {
739		zoomy1 =   ((float)drw_h/(float)src_h);
740		zoomy2 = ( ((float)drw_h/(float)src_h) - (int)zoomy1 ) * 1024;
741		OUTW(vgaIOBase + 4, (zoomy2&0xff)<<8 | 0x82);
742		OUTW(vgaIOBase + 4, (zoomy2&0x0300)|
743		     (((int)((float)src_h/(float)drw_h)-1)&7)<<10 | 0x8083);
744	    }
745    }
746
747    if (pTrident->Chipset >= CYBER9388) {
748	int lb = (width+2) >> 2;
749
750    	OUTW(vgaIOBase + 4, ((lb & 0x100)>>1) | 0x0895);
751    	OUTW(vgaIOBase + 4,  (lb & 0xFF)<<8   | 0x0096);
752    	if ((pTrident->videoFlags & VID_DOUBLE_LINEBUFFER_FOR_WIDE_SRC)
753	      && (src_w > 384)) {
754    	    OUTW(0x3C4, 0x0497); /* 2x line buffers */
755    	} else {
756    	    OUTW(0x3C4, 0x0097); /* 1x line buffers */
757    	}
758    	OUTW(vgaIOBase + 4, 0x0097);
759    	OUTW(vgaIOBase + 4, 0x00BA);
760    	OUTW(vgaIOBase + 4, 0x00BB);
761    	OUTW(vgaIOBase + 4, 0xFFBC);
762    	OUTW(vgaIOBase + 4, 0xFFBD);
763    	OUTW(vgaIOBase + 4, 0x04BE);
764    	OUTW(vgaIOBase + 4, 0x948E);
765    } else {
766
767    	OUTW(vgaIOBase + 4, ((((id == FOURCC_YV12) || (id == FOURCC_YUY2))
768					? (width >> 2) : (width >> 6)) << 8) | 0x95);
769    	OUTW(vgaIOBase + 4, ((((id == FOURCC_YV12) || (id == FOURCC_YUY2))
770				? ((width+2) >> 2) : ((width+2) >> 6)) << 8) |0x96);
771
772    	OUTW(vgaIOBase + 4, 0x948E);
773	OUTB(0x83C8, 0x00);
774	OUTB(0x83C6, 0x95);
775    }
776}
777
778static int
779TRIDENTPutImage(
780  ScrnInfoPtr pScrn,
781  short src_x, short src_y,
782  short drw_x, short drw_y,
783  short src_w, short src_h,
784  short drw_w, short drw_h,
785  int id, unsigned char* buf,
786  short width, short height,
787  Bool sync,
788  RegionPtr clipBoxes, pointer data,
789  DrawablePtr pDraw
790){
791   TRIDENTPortPrivPtr pPriv = (TRIDENTPortPrivPtr)data;
792   TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
793   INT32 x1, x2, y1, y2;
794   unsigned char *dst_start;
795   int new_size, offset, offset2 = 0, offset3 = 0;
796   int srcPitch, srcPitch2 = 0, dstPitch;
797   int top, left, npixels, nlines, bpp;
798   BoxRec dstBox;
799   CARD32 tmp;
800
801   /* Clip */
802   x1 = src_x;
803   x2 = src_x + src_w;
804   y1 = src_y;
805   y2 = src_y + src_h;
806
807   dstBox.x1 = drw_x;
808   dstBox.x2 = drw_x + drw_w;
809   dstBox.y1 = drw_y;
810   dstBox.y2 = drw_y + drw_h;
811
812   if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes,
813								width, height))
814	return Success;
815
816   dstBox.x1 -= pScrn->frameX0;
817   dstBox.x2 -= pScrn->frameX0;
818   dstBox.y1 -= pScrn->frameY0;
819   dstBox.y2 -= pScrn->frameY0;
820
821   bpp = pScrn->bitsPerPixel >> 3;
822
823   dstPitch = ((width << 1) + 15) & ~15;
824   new_size = ((dstPitch * height) + bpp - 1) / bpp;
825   switch(id) {
826   case FOURCC_YV12:
827   case FOURCC_I420:
828	srcPitch = (width + 3) & ~3;
829	offset2 = srcPitch * height;
830	srcPitch2 = ((width >> 1) + 3) & ~3;
831	offset3 = (srcPitch2 * (height >> 1)) + offset2;
832	break;
833   case FOURCC_UYVY:
834   case FOURCC_YUY2:
835   default:
836	srcPitch = (width << 1);
837	break;
838   }
839
840   if(!(pPriv->linear = TRIDENTAllocateMemory(pScrn, pPriv->linear, new_size)))
841	return BadAlloc;
842
843    /* copy data */
844   top = y1 >> 16;
845   left = (x1 >> 16) & ~1;
846   npixels = ((((x2 + 0xffff) >> 16) + 1) & ~1) - left;
847   left <<= 1;
848
849   offset = pPriv->linear->offset * bpp;
850
851   dst_start = pTrident->FbBase + offset + left + (top * dstPitch);
852
853   switch(id) {
854    case FOURCC_YV12:
855    case FOURCC_I420:
856	top &= ~1;
857	tmp = ((top >> 1) * srcPitch2) + (left >> 2);
858	offset2 += tmp;
859	offset3 += tmp;
860	if(id == FOURCC_I420) {
861	   tmp = offset2;
862	   offset2 = offset3;
863	   offset3 = tmp;
864	}
865	nlines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top;
866	xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + (left >> 1),
867				buf + offset2, buf + offset3, dst_start,
868				srcPitch, srcPitch2, dstPitch, nlines, npixels);
869	break;
870    case FOURCC_UYVY:
871    case FOURCC_YUY2:
872    default:
873	buf += (top * srcPitch) + left;
874	nlines = ((y2 + 0xffff) >> 16) - top;
875	xf86XVCopyPacked(buf, dst_start, srcPitch, dstPitch, nlines, npixels);
876        break;
877    }
878
879    /* update cliplist */
880    if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) {
881    	/* update cliplist */
882        REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes);
883        xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey, clipBoxes);
884    }
885
886    offset += top * dstPitch;
887
888    /* Fix video position when using doublescan */
889    if(pScrn->currentMode->Flags & V_DBLSCAN) {
890	    dstBox.y1 <<= 1;
891	    dstBox.y2 <<= 1;
892	    drw_h <<= 1;
893    }
894
895    tridentFixFrame(pScrn,&pPriv->fixFrame);
896    TRIDENTDisplayVideo(pScrn, id, offset, width, height, dstPitch,
897	     x1, y1, x2, y2, &dstBox, src_w, src_h, drw_w, drw_h);
898
899    pPriv->videoStatus = CLIENT_VIDEO_ON;
900
901    pTrident->VideoTimerCallback = TRIDENTVideoTimerCallback;
902
903    return Success;
904}
905
906static int
907TRIDENTQueryImageAttributes(
908  ScrnInfoPtr pScrn,
909  int id,
910  unsigned short *w, unsigned short *h,
911  int *pitches, int *offsets
912){
913    int size, tmp;
914
915    if(*w > 1024) *w = 1024;
916    if(*h > 1024) *h = 1024;
917
918    *w = (*w + 1) & ~1;
919    if(offsets) offsets[0] = 0;
920
921    switch(id) {
922    case FOURCC_YV12:		/* YV12 */
923	*h = (*h + 1) & ~1;
924	size = (*w + 3) & ~3;
925	if(pitches) pitches[0] = size;
926	size *= *h;
927	if(offsets) offsets[1] = size;
928	tmp = ((*w >> 1) + 3) & ~3;
929	if(pitches) pitches[1] = pitches[2] = tmp;
930	tmp *= (*h >> 1);
931	size += tmp;
932	if(offsets) offsets[2] = size;
933	size += tmp;
934	break;
935    default:			/* RGB15, RGB16, YUY2 */
936	size = *w << 1;
937	if(pitches) pitches[0] = size;
938	size *= *h;
939	break;
940    }
941
942    return size;
943}
944
945/****************** Offscreen stuff ***************/
946
947typedef struct {
948  FBLinearPtr linear;
949  Bool isOn;
950} OffscreenPrivRec, * OffscreenPrivPtr;
951
952static int
953TRIDENTAllocateSurface(
954    ScrnInfoPtr pScrn,
955    int id,
956    unsigned short w,
957    unsigned short h,
958    XF86SurfacePtr surface
959){
960    FBLinearPtr linear;
961    int pitch, size, bpp;
962    OffscreenPrivPtr pPriv;
963
964    if((w > 1024) || (h > 1024))
965	return BadAlloc;
966
967    w = (w + 1) & ~1;
968    pitch = ((w << 1) + 15) & ~15;
969    bpp = pScrn->bitsPerPixel >> 3;
970    size = ((pitch * h) + bpp - 1) / bpp;
971
972    if(!(linear = TRIDENTAllocateMemory(pScrn, NULL, size)))
973	return BadAlloc;
974
975    surface->width = w;
976    surface->height = h;
977
978    if(!(surface->pitches = malloc(sizeof(int)))) {
979	xf86FreeOffscreenLinear(linear);
980	return BadAlloc;
981    }
982    if(!(surface->offsets = malloc(sizeof(int)))) {
983	free(surface->pitches);
984	xf86FreeOffscreenLinear(linear);
985	return BadAlloc;
986    }
987    if(!(pPriv = malloc(sizeof(OffscreenPrivRec)))) {
988	free(surface->pitches);
989	free(surface->offsets);
990	xf86FreeOffscreenLinear(linear);
991	return BadAlloc;
992    }
993
994    pPriv->linear = linear;
995    pPriv->isOn = FALSE;
996
997    surface->pScrn = pScrn;
998    surface->id = id;
999    surface->pitches[0] = pitch;
1000    surface->offsets[0] = linear->offset * bpp;
1001    surface->devPrivate.ptr = (pointer)pPriv;
1002
1003    return Success;
1004}
1005
1006static int
1007TRIDENTStopSurface(
1008    XF86SurfacePtr surface
1009){
1010    OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
1011
1012    if(pPriv->isOn) {
1013	TRIDENTPtr pTrident = TRIDENTPTR(surface->pScrn);
1014    	int vgaIOBase = VGAHWPTR(surface->pScrn)->IOBase;
1015	WaitForVBlank(surface->pScrn);
1016 	OUTW(vgaIOBase + 4, 0x848E);
1017	OUTW(vgaIOBase + 4, 0x0091);
1018	pPriv->isOn = FALSE;
1019    }
1020
1021    return Success;
1022}
1023
1024
1025static int
1026TRIDENTFreeSurface(
1027    XF86SurfacePtr surface
1028){
1029    OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
1030
1031    if(pPriv->isOn)
1032	TRIDENTStopSurface(surface);
1033    xf86FreeOffscreenLinear(pPriv->linear);
1034    free(surface->pitches);
1035    free(surface->offsets);
1036    free(surface->devPrivate.ptr);
1037
1038    return Success;
1039}
1040
1041static int
1042TRIDENTGetSurfaceAttribute(
1043    ScrnInfoPtr pScrn,
1044    Atom attribute,
1045    INT32 *value
1046){
1047    return TRIDENTGetPortAttribute(pScrn, attribute, value,
1048			(pointer)(GET_PORT_PRIVATE(pScrn)));
1049}
1050
1051static int
1052TRIDENTSetSurfaceAttribute(
1053    ScrnInfoPtr pScrn,
1054    Atom attribute,
1055    INT32 value
1056){
1057    return TRIDENTSetPortAttribute(pScrn, attribute, value,
1058			(pointer)(GET_PORT_PRIVATE(pScrn)));
1059}
1060
1061static int
1062TRIDENTDisplaySurface(
1063    XF86SurfacePtr surface,
1064    short src_x, short src_y,
1065    short drw_x, short drw_y,
1066    short src_w, short src_h,
1067    short drw_w, short drw_h,
1068    RegionPtr clipBoxes
1069){
1070    OffscreenPrivPtr pPriv = (OffscreenPrivPtr)surface->devPrivate.ptr;
1071    ScrnInfoPtr pScrn = surface->pScrn;
1072    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
1073    TRIDENTPortPrivPtr portPriv = pTrident->adaptor->pPortPrivates[0].ptr;
1074    INT32 x1, y1, x2, y2;
1075    BoxRec dstBox;
1076
1077    x1 = src_x;
1078    x2 = src_x + src_w;
1079    y1 = src_y;
1080    y2 = src_y + src_h;
1081
1082    dstBox.x1 = drw_x;
1083    dstBox.x2 = drw_x + drw_w;
1084    dstBox.y1 = drw_y;
1085    dstBox.y2 = drw_y + drw_h;
1086
1087    if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes,
1088			surface->width, surface->height))
1089    {
1090	return Success;
1091    }
1092
1093    dstBox.x1 -= pScrn->frameX0;
1094    dstBox.x2 -= pScrn->frameX0;
1095    dstBox.y1 -= pScrn->frameY0;
1096    dstBox.y2 -= pScrn->frameY0;
1097
1098    TRIDENTResetVideo(pScrn);
1099
1100    tridentFixFrame(pScrn,&portPriv->fixFrame);
1101    TRIDENTDisplayVideo(pScrn, surface->id, surface->offsets[0],
1102	     surface->width, surface->height, surface->pitches[0],
1103	     x1, y1, x2, y2, &dstBox, src_w, src_h, drw_w, drw_h);
1104
1105    xf86XVFillKeyHelper(pScrn->pScreen, portPriv->colorKey, clipBoxes);
1106
1107    pPriv->isOn = TRUE;
1108    /* we've prempted the XvImage stream so set its free timer */
1109    if(portPriv->videoStatus & CLIENT_VIDEO_ON) {
1110	REGION_EMPTY(pScrn->pScreen, &portPriv->clip);
1111	UpdateCurrentTime();
1112	portPriv->videoStatus = FREE_TIMER;
1113	portPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
1114	pTrident->VideoTimerCallback = TRIDENTVideoTimerCallback;
1115    }
1116
1117    return Success;
1118}
1119
1120static void
1121TRIDENTInitOffscreenImages(ScreenPtr pScreen)
1122{
1123    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1124    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
1125    XF86OffscreenImagePtr offscreenImages;
1126
1127    /* need to free this someplace */
1128    if(!(offscreenImages = malloc(sizeof(XF86OffscreenImageRec))))
1129	return;
1130
1131    offscreenImages[0].image = &Images[0];
1132    offscreenImages[0].flags = VIDEO_OVERLAID_IMAGES |
1133			       VIDEO_CLIP_TO_VIEWPORT;
1134    offscreenImages[0].alloc_surface = TRIDENTAllocateSurface;
1135    offscreenImages[0].free_surface = TRIDENTFreeSurface;
1136    offscreenImages[0].display = TRIDENTDisplaySurface;
1137    offscreenImages[0].stop = TRIDENTStopSurface;
1138    offscreenImages[0].setAttribute = TRIDENTSetSurfaceAttribute;
1139    offscreenImages[0].getAttribute = TRIDENTGetSurfaceAttribute;
1140    offscreenImages[0].max_width = 1024;
1141    offscreenImages[0].max_height = 1024;
1142    if (pTrident->Chipset >= CYBER9388) {
1143    	offscreenImages[0].num_attributes = NUM_ATTRIBUTES;
1144    } else {
1145    	offscreenImages[0].num_attributes = 1; /* just colorkey */
1146    }
1147    offscreenImages[0].attributes = Attributes;
1148
1149    xf86XVRegisterOffscreenImages(pScreen, offscreenImages, 1);
1150}
1151
1152static void
1153TRIDENTVideoTimerCallback(ScrnInfoPtr pScrn, Time time)
1154{
1155    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
1156    TRIDENTPortPrivPtr pPriv = pTrident->adaptor->pPortPrivates[0].ptr;
1157    int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
1158
1159    if(pPriv->videoStatus & TIMER_MASK) {
1160	if(pPriv->videoStatus & OFF_TIMER) {
1161	    if(pPriv->offTime < time) {
1162		WaitForVBlank(pScrn);
1163  		OUTW(vgaIOBase + 4, 0x848E);
1164		OUTW(vgaIOBase + 4, 0x0091);
1165		pPriv->videoStatus = FREE_TIMER;
1166		pPriv->freeTime = time + FREE_DELAY;
1167	    }
1168	} else {  /* FREE_TIMER */
1169	    if(pPriv->freeTime < time) {
1170		if(pPriv->linear) {
1171		   xf86FreeOffscreenLinear(pPriv->linear);
1172		   pPriv->linear = NULL;
1173		}
1174		pPriv->videoStatus = 0;
1175	        pTrident->VideoTimerCallback = NULL;
1176	    }
1177        }
1178    } else  /* shouldn't get here */
1179	pTrident->VideoTimerCallback = NULL;
1180}
1181
1182    /* Calculate skew offsets for video overlay */
1183
1184
1185void
1186tridentFixFrame(ScrnInfoPtr pScrn, int *fixFrame)
1187{
1188
1189    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
1190    int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
1191    int HTotal, HSyncStart;
1192    int VTotal, VSyncStart;
1193    int h_off = 0;
1194    int v_off = 0;
1195    unsigned char CRTC[0x11];
1196    unsigned char hcenter, vcenter;
1197    Bool isShadow;
1198    unsigned char shadow = 0;
1199
1200    if ((*fixFrame)++ < 100)
1201	return;
1202
1203  *fixFrame = 0;
1204
1205  OUTB(0x3CE, CyberControl);
1206  isShadow = ((INB(0x3CF) & 0x81) == 0x81);
1207
1208  if (isShadow)
1209      SHADOW_ENABLE(shadow);
1210
1211    OUTB(vgaIOBase + 4, 0x0);
1212    CRTC[0x0] = INB(vgaIOBase + 5);
1213    OUTB(vgaIOBase + 4, 0x4);
1214    CRTC[0x4] = INB(vgaIOBase + 5);
1215    OUTB(vgaIOBase + 4, 0x5);
1216    CRTC[0x5] = INB(vgaIOBase + 5);
1217    OUTB(vgaIOBase + 4, 0x6);
1218    CRTC[0x6] = INB(vgaIOBase + 5);
1219    OUTB(vgaIOBase + 4, 0x7);
1220    CRTC[0x7] = INB(vgaIOBase + 5);
1221    OUTB(vgaIOBase + 4, 0x10);
1222    CRTC[0x10] = INB(vgaIOBase + 5);
1223    OUTB(0x3CE, HorStretch);
1224    hcenter = INB(0x3CF);
1225    OUTB(0x3CE, VertStretch);
1226    vcenter = INB(0x3CF);
1227
1228    HTotal = CRTC[0] << 3;
1229    VTotal = CRTC[6]
1230	| ((CRTC[7] & (1<<0)) << 8)
1231	| ((CRTC[7] & (1<<5)) << 4);
1232    HSyncStart = (CRTC[4]
1233		  + ((CRTC[5] >> 5) & 0x3)) << 3;
1234    VSyncStart = CRTC[0x10]
1235	| ((CRTC[7] & (1<<2)) << 6)
1236	| ((CRTC[7] & (1<<7)) << 2);
1237
1238    if (isShadow) {
1239	SHADOW_RESTORE(shadow);
1240	if (pTrident->lcdMode != 0xff) {
1241	    if (hcenter & 0x80) {
1242	    h_off = (LCD[pTrident->lcdMode].display_x
1243		     - pScrn->currentMode->HDisplay) >> 1;
1244		switch (pTrident->Chipset) {
1245		    case BLADEXP:
1246			h_off -= 5;
1247		}
1248	    }
1249	    if (vcenter & 0x80) {
1250	    v_off = (LCD[pTrident->lcdMode].display_y
1251		     - pScrn->currentMode->VDisplay) >> 1;
1252	}
1253    }
1254    }
1255
1256    pTrident->hsync = HTotal - HSyncStart + 23 + h_off;
1257    pTrident->vsync = VTotal - VSyncStart - 2 + v_off;
1258    pTrident->hsync_rskew = 0;
1259    pTrident->vsync_bskew = 0;
1260
1261    /*
1262     * HACK !! As awful as this is, it appears to be the only way....Sigh!
1263     * We have XvHsync and XvVsync as options now, which adjust
1264     * at the very end of this function. It'll be helpful for now
1265     * and we can get more data on some of these skew values.
1266     */
1267    switch (pTrident->Chipset) {
1268	case TGUI9680:
1269	    /* Further tweaking needed */
1270    	    pTrident->hsync -= 84;
1271	    pTrident->vsync += 2;
1272	    break;
1273	case PROVIDIA9682:
1274	    /* Further tweaking needed */
1275	    pTrident->hsync += 7;
1276	    break;
1277	case PROVIDIA9685:
1278	    /* Spot on */
1279	    break;
1280        case BLADEXP:
1281        case CYBERBLADEXPAI1:
1282	    pTrident->hsync -= 15;
1283	    pTrident->hsync_rskew = 3;
1284	    break;
1285	case BLADE3D:
1286	    if (pScrn->depth == 24)
1287		pTrident->hsync -= 8;
1288	    else
1289		pTrident->hsync -= 6;
1290	    break;
1291	case CYBERBLADEI7:
1292	case CYBERBLADEI7D:
1293	case CYBERBLADEI1:
1294	case CYBERBLADEI1D:
1295	    if (pScrn->depth == 24)
1296		pTrident->hsync -= 7;
1297	    else
1298		pTrident->hsync -= 6;
1299	    break;
1300	case CYBERBLADEAI1:
1301	    pTrident->hsync -= 7;
1302	    break;
1303	case CYBERBLADEAI1D:
1304	    pTrident->vsync += 2;
1305	    pTrident->vsync_bskew = -4;
1306	    pTrident->hsync -= 5;
1307	    break;
1308	case CYBERBLADEE4:
1309	    pTrident->hsync -= 8;
1310	    break;
1311	case CYBERBLADEXP4:
1312	    pTrident->hsync -= 24;
1313	    pTrident->hsync_rskew = -1;
1314	    break;
1315	case CYBER9397:
1316	    pTrident->hsync -= 1;
1317  	    pTrident->vsync -= 0;
1318	    pTrident->vsync_bskew = 0;
1319	    break;
1320	case CYBER9397DVD:
1321	    pTrident->hsync_rskew = -1;
1322	    pTrident->vsync_bskew = -1;
1323	    break;
1324    }
1325    pTrident->hsync+=pTrident->OverrideHsync;
1326    pTrident->vsync+=pTrident->OverrideVsync;
1327    pTrident->hsync_rskew += pTrident->OverrideRskew;
1328    pTrident->vsync_bskew += pTrident->OverrideBskew;
1329}
1330
1331static void
1332WaitForVBlank(ScrnInfoPtr pScrn)
1333{
1334    register vgaHWPtr hwp = VGAHWPTR(pScrn);
1335
1336    /* We have to wait for one full VBlank to let the video engine start/stop.
1337     * So the first may be waiting for too short a period as it may already
1338     * be part way through the video frame. So we wait a second time to ensure
1339     * full vblank has passed.
1340     * - Alan.
1341     */
1342#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
1343    if (!xf86IsPc98())
1344#endif
1345    {
1346       WAITFORVSYNC;
1347       WAITFORVSYNC;
1348    }
1349}
1350