lx_video.c revision 170d5fdc
1/* Copyright (c) 2007-2008 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 *
21 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 */
25
26/* TODO:
27   Add rotation
28   Add back in double buffering?
29
30*/
31
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
36#include <stdlib.h>
37#include <string.h>
38
39#include "xf86.h"
40#include "xf86_OSproc.h"
41#include "compiler.h"
42#include "xf86PciInfo.h"
43#include "xf86Pci.h"
44#include "xf86fbman.h"
45#include "regionstr.h"
46#include "dixstruct.h"
47
48#include "geode.h"
49#include "xf86xv.h"
50#include <X11/extensions/Xv.h>
51#include "fourcc.h"
52#include "geode_fourcc.h"
53#include "cim/cim_defs.h"
54#include "cim/cim_regs.h"
55
56#define OFF_DELAY 		200
57#define FREE_DELAY 		60000
58#define OFF_TIMER 		0x01
59#define FREE_TIMER		0x02
60#define CLIENT_VIDEO_ON	0x04
61#define TIMER_MASK      (OFF_TIMER | FREE_TIMER)
62
63#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
64#define ARRAY_SIZE(a) (sizeof((a)) / (sizeof(*(a))))
65
66/* Local function prototypes */
67static void LXStopVideo(ScrnInfoPtr pScrni, pointer data, Bool exit);
68
69static void
70LXDisplayVideo(ScrnInfoPtr pScrni, int id, short width, short height,
71    BoxPtr dstBox, short srcW, short srcH, short drawW, short drawH);
72
73static void LXResetVideo(ScrnInfoPtr pScrni);
74
75static XF86VideoEncodingRec DummyEncoding[1] = {
76    {0, "XV_IMAGE", 1024, 1024, {1, 1}}
77};
78
79static XF86VideoFormatRec Formats[] = {
80    {8, PseudoColor}, {15, TrueColor}, {16, TrueColor}, {24, TrueColor}
81};
82
83static XF86AttributeRec Attributes[] = {
84    {XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"},
85    {XvSettable | XvGettable, 0, 1, "XV_FILTER"},
86    {XvSettable | XvGettable, 0, 1, "XV_COLORKEYMODE"}
87};
88
89static XF86ImageRec Images[] = {
90    XVIMAGE_UYVY,
91    XVIMAGE_YUY2,
92    XVIMAGE_Y2YU,
93    XVIMAGE_YVYU,
94    XVIMAGE_Y800,
95    XVIMAGE_I420,
96    XVIMAGE_YV12,
97    XVIMAGE_RGB565
98};
99
100typedef struct
101{
102    ExaOffscreenArea *vidmem;
103    RegionRec clip;
104    CARD32 filter;
105    CARD32 colorKey;
106    CARD32 colorKeyMode;
107    CARD32 videoStatus;
108    Time offTime;
109    Time freeTime;
110    short pwidth, pheight;
111} GeodePortPrivRec, *GeodePortPrivPtr;
112
113#define GET_PORT_PRIVATE(pScrni) \
114   (GeodePortPrivRec *)((GEODEPTR(pScrni))->adaptor->pPortPrivates[0].ptr)
115
116static void
117LXCopyFromSys(GeodeRec * pGeode, unsigned char *src, unsigned int dst,
118    int dstPitch, int srcPitch, int h, int w)
119{
120
121    gp_declare_blt(0);
122    gp_set_bpp((srcPitch / w) << 3);
123
124    gp_set_raster_operation(0xCC);
125    gp_set_strides(dstPitch, srcPitch);
126    gp_set_solid_pattern(0);
127
128    gp_color_bitmap_to_screen_blt(dst, 0, w, h, src, srcPitch);
129}
130
131static void
132LXSetColorkey(ScrnInfoPtr pScrni, GeodePortPrivRec * pPriv)
133{
134    int red, green, blue;
135    unsigned long key;
136
137    switch (pScrni->depth) {
138    case 8:
139	vg_get_display_palette_entry(pPriv->colorKey & 0xFF, &key);
140	red = ((key >> 16) & 0xFF);
141	green = ((key >> 8) & 0xFF);
142	blue = (key & 0xFF);
143	break;
144    case 16:
145	red = (pPriv->colorKey & pScrni->mask.red) >>
146	    pScrni->offset.red << (8 - pScrni->weight.red);
147	green = (pPriv->colorKey & pScrni->mask.green) >>
148	    pScrni->offset.green << (8 - pScrni->weight.green);
149	blue = (pPriv->colorKey & pScrni->mask.blue) >>
150	    pScrni->offset.blue << (8 - pScrni->weight.blue);
151	break;
152    default:
153	/* for > 16 bpp we send in the mask in xf86SetWeight. This
154	 * function is providing the offset by 1 more. So we take
155	 * this as a special case and subtract 1 for > 16
156	 */
157
158	red = (pPriv->colorKey & pScrni->mask.red) >>
159	    (pScrni->offset.red - 1) << (8 - pScrni->weight.red);
160	green = (pPriv->colorKey & pScrni->mask.green) >>
161	    (pScrni->offset.green - 1) << (8 - pScrni->weight.green);
162	blue = (pPriv->colorKey & pScrni->mask.blue) >>
163	    (pScrni->offset.blue - 1) << (8 - pScrni->weight.blue);
164	break;
165    }
166
167    df_set_video_color_key((blue | (green << 8) | (red << 16)),
168	0xFFFFFF, (pPriv->colorKeyMode == 0));
169
170    REGION_EMPTY(pScrni->pScreen, &pPriv->clip);
171}
172
173/* A structure full of the scratch information that originates in the copy routines,
174   but is needed for the video display - maybe we should figure out a way to attach
175   this to structures?  I hate to put it in pGeode since it will increase the size of
176   the structure, and possibly cause us cache issues.
177*/
178
179struct
180{
181    unsigned int dstOffset;
182    unsigned int dstPitch;
183    unsigned int UVPitch;
184    unsigned int UDstOffset;
185    unsigned int VDstOffset;
186} videoScratch;
187
188/* Copy planar YUV data */
189
190static Bool
191LXAllocateVidMem(ScrnInfoPtr pScrni, GeodePortPrivRec *pPriv, int size)
192{
193    if (!pPriv->vidmem || pPriv->vidmem->size < size) {
194	if (pPriv->vidmem) {
195		exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
196		pPriv->vidmem = NULL;
197	}
198
199	pPriv->vidmem = exaOffscreenAlloc(pScrni->pScreen, size, 4,
200			TRUE, NULL, NULL);
201
202    	if (pPriv->vidmem == NULL) {
203		ErrorF("Could not allocate memory for the video\n");
204		return FALSE;
205    	}
206    }
207
208    return TRUE;
209}
210
211static Bool
212LXCopyPlanar(ScrnInfoPtr pScrni, int id, unsigned char *buf,
213    short x1, short y1, short x2, short y2,
214    int width, int height, pointer data)
215{
216    GeodeRec *pGeode = GEODEPTR(pScrni);
217    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
218
219    unsigned int YSrcPitch, YDstPitch;
220    unsigned int UVSrcPitch, UVDstPitch;
221    unsigned int YSrcOffset, YDstOffset;
222    unsigned int USrcOffset, UDstOffset;
223    unsigned int VSrcOffset, VDstOffset;
224
225    unsigned int size, lines, top, left, pixels;
226
227    YSrcPitch = (width + 3) & ~3;
228    YDstPitch = (width + 31) & ~31;
229
230    UVSrcPitch = ((width >> 1) + 3) & ~3;
231    UVDstPitch = ((width >> 1) + 15) & ~15;
232
233    USrcOffset = YSrcPitch * height;
234    VSrcOffset = USrcOffset + (UVSrcPitch * (height >> 1));
235
236    UDstOffset = YDstPitch * height;
237    VDstOffset = UDstOffset + (UVDstPitch * (height >> 1));
238
239    size = YDstPitch * height;
240    size += UVDstPitch * height;
241
242    if (LXAllocateVidMem(pScrni, pPriv, size) == FALSE) {
243	ErrorF("Error allocating an offscreen Planar region.\n");
244	return FALSE;
245    }
246
247    /* The top of the source region we want to copy */
248    top = y1 & ~1;
249
250    /* The left hand side of the source region, aligned on a word */
251    left = x1 & ~1;
252
253    /* Number of bytes to copy, also word aligned */
254    pixels = ((x2 + 1) & ~1) - left;
255
256    /* Calculate the source offset */
257    YSrcOffset = (top * YSrcPitch) + left;
258    USrcOffset += ((top >> 1) * UVSrcPitch) + (left >> 1);
259    VSrcOffset += ((top >> 1) * UVSrcPitch) + (left >> 1);
260
261    /* Calculate the destination offset */
262    YDstOffset = (top * YDstPitch) + left;
263    UDstOffset += ((top >> 1) * UVDstPitch) + (left >> 1);
264    VDstOffset += ((top >> 1) * UVDstPitch) + (left >> 1);
265
266    lines = ((y2 + 1) & ~1) - top;
267
268    /* Copy Y */
269
270    LXCopyFromSys(pGeode, buf + YSrcOffset,
271	pPriv->vidmem->offset + YDstOffset, YDstPitch, YSrcPitch, lines,
272	pixels);
273
274    /* Copy U + V at the same time */
275
276    LXCopyFromSys(pGeode, buf + USrcOffset,
277	pPriv->vidmem->offset + UDstOffset, UVDstPitch, UVSrcPitch, lines,
278	pixels >> 1);
279
280    videoScratch.dstOffset = pPriv->vidmem->offset + YDstOffset;
281    videoScratch.dstPitch = YDstPitch;
282    videoScratch.UVPitch = UVDstPitch;
283    videoScratch.UDstOffset = pPriv->vidmem->offset + UDstOffset;
284    videoScratch.VDstOffset = pPriv->vidmem->offset + VDstOffset;
285
286    return TRUE;
287}
288
289static Bool
290LXCopyPacked(ScrnInfoPtr pScrni, int id, unsigned char *buf,
291    short x1, short y1, short x2, short y2,
292    int width, int height, pointer data)
293{
294    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
295    GeodeRec *pGeode = GEODEPTR(pScrni);
296    unsigned int dstPitch, srcPitch;
297    unsigned int srcOffset, dstOffset;
298    unsigned int lines, top, left, pixels;
299
300    dstPitch = ((width << 1) + 3) & ~3;
301    srcPitch = (width << 1);
302
303    lines = ((dstPitch * height) + pGeode->Pitch - 1) / pGeode->Pitch;
304
305    if (LXAllocateVidMem(pScrni, pPriv, lines) == FALSE) {
306	ErrorF("Error allocating an offscreen Packed region.\n");
307	return FALSE;
308    }
309
310    /* The top of the source region we want to copy */
311    top = y1;
312
313    /* The left hand side of the source region, aligned on a word */
314    left = x1 & ~1;
315
316    /* Number of bytes to copy, also word aligned */
317    pixels = ((x2 + 1) & ~1) - left;
318
319    /* Adjust the incoming buffer */
320    srcOffset = (top * srcPitch) + left;
321
322    /* Calculate the destination offset */
323    dstOffset = pPriv->vidmem->offset + (top * dstPitch) + left;
324
325    /* Make the copy happen */
326
327    if (id == FOURCC_Y800) {
328
329	/* Use the shared (unaccelerated) greyscale copy - you could probably
330	 * accelerate it using a 2 pass blit and patterns, but it doesn't really
331	 * seem worth it
332	 */
333
334	GeodeCopyGreyscale(buf + srcOffset, pGeode->FBBase + dstOffset,
335	    srcPitch, dstPitch, height, pixels >> 1);
336    } else
337	LXCopyFromSys(pGeode, buf + srcOffset, dstOffset, dstPitch, srcPitch,
338	    height, pixels);
339
340    videoScratch.dstOffset = dstOffset;
341    videoScratch.dstPitch = dstPitch;
342
343    return TRUE;
344}
345
346static void
347LXDisplayVideo(ScrnInfoPtr pScrni, int id, short width, short height,
348    BoxPtr dstBox, short srcW, short srcH, short drawW, short drawH)
349{
350    long ystart, xend, yend;
351    unsigned long lines = 0;
352    unsigned long yExtra, uvExtra = 0;
353    DF_VIDEO_POSITION vidPos;
354    DF_VIDEO_SOURCE_PARAMS vSrcParams;
355    int err;
356
357    memset(&vSrcParams, 0, sizeof(vSrcParams));
358
359    gp_wait_until_idle();
360
361    switch (id) {
362    case FOURCC_UYVY:
363	vSrcParams.video_format = DF_VIDFMT_UYVY;
364	break;
365
366    case FOURCC_Y800:
367    case FOURCC_YV12:
368    case FOURCC_I420:
369	vSrcParams.video_format = DF_VIDFMT_Y0Y1Y2Y3;
370	break;
371    case FOURCC_YUY2:
372	vSrcParams.video_format = DF_VIDFMT_YUYV;
373	break;
374    case FOURCC_Y2YU:
375	vSrcParams.video_format = DF_VIDFMT_Y2YU;
376	break;
377    case FOURCC_YVYU:
378	vSrcParams.video_format = DF_VIDFMT_YVYU;
379	break;
380    case FOURCC_RGB565:
381	vSrcParams.video_format = DF_VIDFMT_RGB;
382	break;
383    }
384
385    vSrcParams.width = width;
386    vSrcParams.height = height;
387    vSrcParams.y_pitch = videoScratch.dstPitch;
388    vSrcParams.uv_pitch = videoScratch.UVPitch;
389
390    /* Set up scaling */
391    df_set_video_filter_coefficients(NULL, 1);
392
393    err = df_set_video_scale(width, height, drawW, drawH,
394	DF_SCALEFLAG_CHANGEX | DF_SCALEFLAG_CHANGEY);
395    if (err != CIM_STATUS_OK) {
396	/* Note the problem, but do nothing for now. */
397	ErrorF("Video scale factor too large: %dx%d -> %dx%d\n",
398	    width, height, drawW, drawH);
399    }
400
401    /* Figure out clipping */
402
403    xend = dstBox->x2;
404    yend = dstBox->y2;
405
406    if (dstBox->y1 < 0) {
407	if (srcH < drawH)
408	    lines = ((-dstBox->y1) * srcH) / drawH;
409	else
410	    lines = (-dstBox->y1);
411
412	ystart = 0;
413	drawH += dstBox->y1;
414    } else {
415	ystart = dstBox->y1;
416	lines = 0;
417    }
418
419    yExtra = lines * videoScratch.dstPitch;
420    uvExtra = (lines >> 1) * videoScratch.UVPitch;
421
422    memset(&vidPos, 0, sizeof(vidPos));
423
424    vidPos.x = dstBox->x1;
425    vidPos.y = ystart;
426    vidPos.width = xend - dstBox->x1;
427    vidPos.height = yend - ystart;
428
429    df_set_video_position(&vidPos);
430
431    vSrcParams.y_offset = videoScratch.dstOffset + yExtra;
432
433    switch (id) {
434    case FOURCC_Y800:
435    case FOURCC_I420:
436	vSrcParams.u_offset = videoScratch.UDstOffset + uvExtra;
437	vSrcParams.v_offset = videoScratch.VDstOffset + uvExtra;
438	break;
439    case FOURCC_YV12:
440	vSrcParams.v_offset = videoScratch.UDstOffset + uvExtra;
441	vSrcParams.u_offset = videoScratch.VDstOffset + uvExtra;
442	break;
443
444    default:
445	vSrcParams.u_offset = vSrcParams.v_offset = 0;
446	break;
447    }
448
449    vSrcParams.flags = DF_SOURCEFLAG_IMPLICITSCALING;
450    df_configure_video_source(&vSrcParams, &vSrcParams);
451
452    /* Turn on the video palette */
453    df_set_video_palette(NULL);
454    df_set_video_enable(1, 0);
455}
456
457static int
458LXPutImage(ScrnInfoPtr pScrni,
459    short srcX, short srcY, short drawX, short drawY,
460    short srcW, short srcH, short drawW, short drawH,
461    int id, unsigned char *buf,
462    short width, short height, Bool sync, RegionPtr clipBoxes,
463    pointer data, DrawablePtr pDraw)
464{
465    GeodeRec *pGeode = GEODEPTR(pScrni);
466    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
467    INT32 x1, x2, y1, y2;
468    BoxRec dstBox;
469    Bool ret;
470
471    if (pGeode->rotation != RR_Rotate_0)
472	return Success;
473
474    if (srcW <= 0 || srcH <= 0) {
475	return Success;
476    }
477
478    if (drawW <= 0 || drawH <= 0) {
479	return Success;
480    }
481
482    if (drawW > 16384)
483	drawW = 16384;
484
485    memset(&videoScratch, 0, sizeof(videoScratch));
486
487    x1 = srcX;
488    x2 = srcX + srcW;
489    y1 = srcY;
490    y2 = srcY + srcH;
491
492    dstBox.x1 = drawX;
493    dstBox.x2 = drawX + drawW;
494    dstBox.y1 = drawY;
495    dstBox.y2 = drawY + drawH;
496
497    dstBox.x1 -= pScrni->frameX0;
498    dstBox.x2 -= pScrni->frameX0;
499    dstBox.y1 -= pScrni->frameY0;
500    dstBox.y2 -= pScrni->frameY0;
501
502    if (id == FOURCC_YV12 || id == FOURCC_I420)
503	ret = LXCopyPlanar(pScrni, id, buf, x1, y1, x2, y2, width,
504			height, data);
505    else
506	ret = LXCopyPacked(pScrni, id, buf, x1, y1, x2, y2, width,
507			height, data);
508
509    if (ret == FALSE)
510	return BadAlloc;
511
512    if (!RegionsEqual(&pPriv->clip, clipBoxes) ||
513	(drawW != pPriv->pwidth || drawH != pPriv->pheight)) {
514	REGION_COPY(pScrni->pScreen, &pPriv->clip, clipBoxes);
515
516	if (pPriv->colorKeyMode == 0) {
517	    xf86XVFillKeyHelper(pScrni->pScreen, pPriv->colorKey, clipBoxes);
518	}
519
520	LXDisplayVideo(pScrni, id, width, height, &dstBox,
521	    srcW, srcH, drawW, drawH);
522	pPriv->pwidth = drawW;
523	pPriv->pheight = drawH;
524    }
525
526    pPriv->videoStatus = CLIENT_VIDEO_ON;
527
528    return Success;
529}
530
531static void
532LXQueryBestSize(ScrnInfoPtr pScrni, Bool motion,
533    short vidW, short vidH, short drawW, short drawH,
534    unsigned int *retW, unsigned int *retH, pointer data)
535{
536    *retW = drawW > 16384 ? 16384 : drawW;
537    *retH = drawH;
538}
539
540static Atom xvColorKey, xvColorKeyMode, xvFilter;
541
542static int
543LXGetPortAttribute(ScrnInfoPtr pScrni,
544    Atom attribute, INT32 * value, pointer data)
545{
546    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
547
548    if (attribute == xvColorKey)
549	*value = pPriv->colorKey;
550    else if (attribute == xvColorKeyMode)
551	*value = pPriv->colorKeyMode;
552    else if (attribute == xvFilter)
553	*value = pPriv->filter;
554    else
555	return BadMatch;
556
557    return Success;
558}
559
560static int
561LXSetPortAttribute(ScrnInfoPtr pScrni,
562    Atom attribute, INT32 value, pointer data)
563{
564    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
565
566    gp_wait_until_idle();
567
568    if (attribute == xvColorKey) {
569	pPriv->colorKey = value;
570	LXSetColorkey(pScrni, pPriv);
571    } else if (attribute == xvColorKeyMode) {
572	pPriv->colorKeyMode = value;
573	LXSetColorkey(pScrni, pPriv);
574    } else if (attribute == xvFilter) {
575	if ((value < 0) || (value > 1))
576	    return BadValue;
577	pPriv->filter = value;
578    } else
579	return BadMatch;
580
581    return Success;
582}
583
584static void
585LXStopVideo(ScrnInfoPtr pScrni, pointer data, Bool exit)
586{
587    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
588
589    if (pPriv->videoStatus == 0)
590	return;
591
592    REGION_EMPTY(pScrni->pScreen, &pPriv->clip);
593    gp_wait_until_idle();
594
595    if (exit) {
596	if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
597	    unsigned int val;
598
599	    df_set_video_enable(0, 0);
600	    /* Put the LUT back in bypass */
601	    val = READ_VID32(DF_VID_MISC);
602	    WRITE_VID32(DF_VID_MISC, val | DF_GAMMA_BYPASS_BOTH);
603	}
604
605	if (pPriv->vidmem) {
606	    exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
607	    pPriv->vidmem = NULL;
608	}
609
610	pPriv->videoStatus = 0;
611
612	/* Eh? */
613    } else if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
614	pPriv->videoStatus |= OFF_TIMER;
615	pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
616    }
617}
618
619static void
620LXResetVideo(ScrnInfoPtr pScrni)
621{
622    GeodeRec *pGeode = GEODEPTR(pScrni);
623
624    if (!pGeode->NoAccel) {
625	GeodePortPrivRec *pPriv = pGeode->adaptor->pPortPrivates[0].ptr;
626
627	gp_wait_until_idle();
628	df_set_video_palette(NULL);
629
630	LXSetColorkey(pScrni, pPriv);
631    }
632}
633
634static void
635LXVidBlockHandler(int i, pointer blockData, pointer pTimeout,
636    pointer pReadmask)
637{
638    ScreenPtr pScrn = screenInfo.screens[i];
639    ScrnInfoPtr pScrni = xf86Screens[i];
640    GeodeRec *pGeode = GEODEPTR(pScrni);
641    GeodePortPrivRec *pPriv = GET_PORT_PRIVATE(pScrni);
642
643    pScrn->BlockHandler = pGeode->BlockHandler;
644    (*pScrn->BlockHandler) (i, blockData, pTimeout, pReadmask);
645    pScrn->BlockHandler = LXVidBlockHandler;
646
647    if (pPriv->videoStatus & TIMER_MASK) {
648	Time now = currentTime.milliseconds;
649
650	if (pPriv->videoStatus & OFF_TIMER) {
651	    gp_wait_until_idle();
652
653	    if (pPriv->offTime < now) {
654		unsigned int val;
655
656		df_set_video_enable(0, 0);
657		pPriv->videoStatus = FREE_TIMER;
658		pPriv->freeTime = now + FREE_DELAY;
659
660		/* Turn off the video palette */
661		val = READ_VID32(DF_VID_MISC);
662		WRITE_VID32(DF_VID_MISC, val | DF_GAMMA_BYPASS_BOTH);
663	    }
664	} else {
665	    if (pPriv->freeTime < now) {
666
667		if (pPriv->vidmem) {
668		    exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
669		    pPriv->vidmem = NULL;
670		}
671
672		pPriv->videoStatus = 0;
673	    }
674	}
675    }
676}
677
678static XF86VideoAdaptorPtr
679LXSetupImageVideo(ScreenPtr pScrn)
680{
681    ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
682    GeodeRec *pGeode = GEODEPTR(pScrni);
683    XF86VideoAdaptorPtr adapt;
684    GeodePortPrivRec *pPriv;
685
686    adapt = calloc(1, sizeof(XF86VideoAdaptorRec) +
687	sizeof(GeodePortPrivRec) + sizeof(DevUnion));
688
689    if (adapt == NULL) {
690	ErrorF("Couldn't create the rec\n");
691	return NULL;
692    }
693
694    adapt->type = XvWindowMask | XvInputMask | XvImageMask;
695    adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
696
697    adapt->name = "AMD Geode LX";
698    adapt->nEncodings = 1;
699    adapt->pEncodings = DummyEncoding;
700    adapt->nFormats = ARRAY_SIZE(Formats);
701    adapt->pFormats = Formats;
702    adapt->nPorts = 1;
703    adapt->pPortPrivates = (DevUnion *) (&adapt[1]);
704    pPriv = (GeodePortPrivRec *) (&adapt->pPortPrivates[1]);
705    adapt->pPortPrivates[0].ptr = (pointer) (pPriv);
706    adapt->pAttributes = Attributes;
707    adapt->nImages = ARRAY_SIZE(Images);
708    adapt->nAttributes = ARRAY_SIZE(Attributes);
709    adapt->pImages = Images;
710    adapt->PutVideo = NULL;
711    adapt->PutStill = NULL;
712    adapt->GetVideo = NULL;
713    adapt->GetStill = NULL;
714    adapt->StopVideo = LXStopVideo;
715    adapt->SetPortAttribute = LXSetPortAttribute;
716    adapt->GetPortAttribute = LXGetPortAttribute;
717    adapt->QueryBestSize = LXQueryBestSize;
718    adapt->PutImage = LXPutImage;
719
720    /* Use the common function */
721    adapt->QueryImageAttributes = GeodeQueryImageAttributes;
722
723    pPriv->vidmem = NULL;
724    pPriv->filter = 0;
725    pPriv->colorKey = 0;
726    pPriv->colorKeyMode = 0;
727    pPriv->videoStatus = 0;
728    pPriv->pwidth = 0;
729    pPriv->pheight = 0;
730
731    REGION_NULL(pScrn, &pPriv->clip);
732
733    pGeode->adaptor = adapt;
734
735    pGeode->BlockHandler = pScrn->BlockHandler;
736    pScrn->BlockHandler = LXVidBlockHandler;
737
738    xvColorKey = MAKE_ATOM("XV_COLORKEY");
739    xvColorKeyMode = MAKE_ATOM("XV_COLORKEYMODE");
740    xvFilter = MAKE_ATOM("XV_FILTER");
741
742    LXResetVideo(pScrni);
743
744    return adapt;
745}
746
747/* Offscreen surface allocation */
748
749struct OffscreenPrivRec
750{
751    ExaOffscreenArea *vidmem;
752    Bool isOn;
753};
754
755static int
756LXDisplaySurface(XF86SurfacePtr surface,
757    short srcX, short srcY, short drawX, short drawY,
758    short srcW, short srcH, short drawW, short drawH, RegionPtr clipBoxes)
759{
760    struct OffscreenPrivRec *pPriv =
761	(struct OffscreenPrivRec *)surface->devPrivate.ptr;
762
763    ScrnInfoPtr pScrni = surface->pScrn;
764    GeodePortPrivRec *portPriv = GET_PORT_PRIVATE(pScrni);
765
766    BoxRec dstBox;
767
768    dstBox.x1 = drawX;
769    dstBox.x2 = drawX + drawW;
770    dstBox.y1 = drawY;
771    dstBox.y2 = drawY + drawH;
772
773    if ((drawW <= 0) | (drawH <= 0))
774	return Success;
775
776    /* Is this still valid? */
777
778    dstBox.x1 -= pScrni->frameX0;
779    dstBox.x2 -= pScrni->frameX0;
780    dstBox.y1 -= pScrni->frameY0;
781    dstBox.y2 -= pScrni->frameY0;
782
783    xf86XVFillKeyHelper(pScrni->pScreen, portPriv->colorKey, clipBoxes);
784
785    videoScratch.dstOffset = surface->offsets[0];
786    videoScratch.dstPitch = surface->pitches[0];
787
788    LXDisplayVideo(pScrni, surface->id, surface->width, surface->height,
789	&dstBox, srcW, srcH, drawW, drawH);
790
791    pPriv->isOn = TRUE;
792
793    if (portPriv->videoStatus & CLIENT_VIDEO_ON) {
794	REGION_EMPTY(pScrni->pScreen, &portPriv->clip);
795	UpdateCurrentTime();
796	portPriv->videoStatus = FREE_TIMER;
797	portPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
798    }
799
800    return Success;
801}
802
803static int
804LXAllocateSurface(ScrnInfoPtr pScrni, int id, unsigned short w,
805    unsigned short h, XF86SurfacePtr surface)
806{
807    GeodeRec *pGeode = GEODEPTR(pScrni);
808    int pitch, lines;
809    ExaOffscreenArea *vidmem;
810    struct OffscreenPrivRec *pPriv;
811
812    if (w > 1024 || h > 1024)
813	return BadAlloc;
814
815    /* The width needs to be word aligned */
816    w = (w + 1) & ~1;
817
818    pitch = ((w << 1) + 15) & ~15;
819    lines = ((pitch * h) + (pGeode->Pitch - 1)) / pGeode->Pitch;
820
821    vidmem = exaOffscreenAlloc(pScrni->pScreen, lines, 4, TRUE,
822		NULL, NULL);
823
824    if (vidmem == NULL) {
825	ErrorF("Error while allocating an offscreen region.\n");
826	return BadAlloc;
827    }
828
829    surface->width = w;
830    surface->height = h;
831
832    surface->pitches = malloc(sizeof(int));
833
834    surface->offsets = malloc(sizeof(int));
835
836    pPriv = malloc(sizeof(struct OffscreenPrivRec));
837
838    if (pPriv && surface->pitches && surface->offsets) {
839
840	pPriv->vidmem = vidmem;
841
842	pPriv->isOn = FALSE;
843
844	surface->pScrn = pScrni;
845	surface->id = id;
846	surface->pitches[0] = pitch;
847	surface->offsets[0] = vidmem->offset;
848	surface->devPrivate.ptr = (pointer) pPriv;
849
850	return Success;
851    }
852
853    if (surface->offsets)
854	free(surface->offsets);
855
856    if (surface->pitches)
857	free(surface->pitches);
858
859    if (vidmem) {
860	exaOffscreenFree(pScrni->pScreen, vidmem);
861	vidmem = NULL;
862    }
863
864    return BadAlloc;
865}
866
867static int
868LXStopSurface(XF86SurfacePtr surface)
869{
870    struct OffscreenPrivRec *pPriv = (struct OffscreenPrivRec *)
871	surface->devPrivate.ptr;
872
873    pPriv->isOn = FALSE;
874    return Success;
875}
876
877static int
878LXFreeSurface(XF86SurfacePtr surface)
879{
880    struct OffscreenPrivRec *pPriv = (struct OffscreenPrivRec *)
881	surface->devPrivate.ptr;
882    ScrnInfoPtr pScrni = surface->pScrn;
883
884    if (pPriv->isOn)
885	LXStopSurface(surface);
886
887    if (pPriv->vidmem) {
888	exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
889	pPriv->vidmem = NULL;
890    }
891
892    free(surface->pitches);
893    free(surface->offsets);
894    free(surface->devPrivate.ptr);
895
896    return Success;
897}
898
899static int
900LXGetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 * value)
901{
902    return LXGetPortAttribute(pScrni, attribute, value,
903	(pointer) (GET_PORT_PRIVATE(pScrni)));
904}
905
906static int
907LXSetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 value)
908{
909    return LXSetPortAttribute(pScrni, attribute, value,
910	(pointer) (GET_PORT_PRIVATE(pScrni)));
911}
912
913static void
914LXInitOffscreenImages(ScreenPtr pScrn)
915{
916    XF86OffscreenImagePtr offscreenImages;
917
918    /* need to free this someplace */
919    if (!(offscreenImages = malloc(sizeof(XF86OffscreenImageRec))))
920	return;
921
922    offscreenImages[0].image = &Images[0];
923    offscreenImages[0].flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
924    offscreenImages[0].alloc_surface = LXAllocateSurface;
925    offscreenImages[0].free_surface = LXFreeSurface;
926    offscreenImages[0].display = LXDisplaySurface;
927    offscreenImages[0].stop = LXStopSurface;
928    offscreenImages[0].setAttribute = LXSetSurfaceAttribute;
929    offscreenImages[0].getAttribute = LXGetSurfaceAttribute;
930    offscreenImages[0].max_width = 1024;
931    offscreenImages[0].max_height = 1024;
932    offscreenImages[0].num_attributes = ARRAY_SIZE(Attributes);
933    offscreenImages[0].attributes = Attributes;
934
935    xf86XVRegisterOffscreenImages(pScrn, offscreenImages, 1);
936}
937
938void
939LXInitVideo(ScreenPtr pScrn)
940{
941    GeodeRec *pGeode;
942    ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
943    XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
944    XF86VideoAdaptorPtr newAdaptor = NULL;
945    int num_adaptors;
946
947    pGeode = GEODEPTR(pScrni);
948
949    if (pGeode->NoAccel) {
950	ErrorF("Cannot run Xv without accelerations!\n");
951	return;
952    }
953
954    if (!(newAdaptor = LXSetupImageVideo(pScrn))) {
955	ErrorF("Error while setting up the adaptor.\n");
956	return;
957    }
958
959    LXInitOffscreenImages(pScrn);
960
961    num_adaptors = xf86XVListGenericAdaptors(pScrni, &adaptors);
962
963    if (!num_adaptors) {
964	num_adaptors = 1;
965	adaptors = &newAdaptor;
966    } else {
967	newAdaptors =
968	    malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr *));
969
970	if (newAdaptors) {
971	    memcpy(newAdaptors, adaptors, num_adaptors *
972		sizeof(XF86VideoAdaptorPtr));
973	    newAdaptors[num_adaptors] = newAdaptor;
974	    adaptors = newAdaptors;
975	    num_adaptors++;
976	} else
977	    ErrorF("Memory error while setting up the adaptor\n");
978    }
979
980    if (num_adaptors)
981	xf86XVScreenInit(pScrn, adaptors, num_adaptors);
982
983    if (newAdaptors)
984	free(newAdaptors);
985}
986