lx_video.c revision 7aef237f
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, dstPitch * height) == 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	/* FIXME: should lines be used here instead of height? */
338	LXCopyFromSys(pGeode, buf + srcOffset, dstOffset, dstPitch, srcPitch,
339	    height, pixels);
340
341    videoScratch.dstOffset = dstOffset;
342    videoScratch.dstPitch = dstPitch;
343
344    return TRUE;
345}
346
347static void
348LXDisplayVideo(ScrnInfoPtr pScrni, int id, short width, short height,
349    BoxPtr dstBox, short srcW, short srcH, short drawW, short drawH)
350{
351    long ystart, xend, yend;
352    unsigned long lines = 0;
353    unsigned long yExtra, uvExtra = 0;
354    DF_VIDEO_POSITION vidPos;
355    DF_VIDEO_SOURCE_PARAMS vSrcParams;
356    int err;
357
358    memset(&vSrcParams, 0, sizeof(vSrcParams));
359
360    gp_wait_until_idle();
361
362    switch (id) {
363    case FOURCC_UYVY:
364	vSrcParams.video_format = DF_VIDFMT_UYVY;
365	break;
366
367    case FOURCC_Y800:
368    case FOURCC_YV12:
369    case FOURCC_I420:
370	vSrcParams.video_format = DF_VIDFMT_Y0Y1Y2Y3;
371	break;
372    case FOURCC_YUY2:
373	vSrcParams.video_format = DF_VIDFMT_YUYV;
374	break;
375    case FOURCC_Y2YU:
376	vSrcParams.video_format = DF_VIDFMT_Y2YU;
377	break;
378    case FOURCC_YVYU:
379	vSrcParams.video_format = DF_VIDFMT_YVYU;
380	break;
381    case FOURCC_RGB565:
382	vSrcParams.video_format = DF_VIDFMT_RGB;
383	break;
384    }
385
386    vSrcParams.width = width;
387    vSrcParams.height = height;
388    vSrcParams.y_pitch = videoScratch.dstPitch;
389    vSrcParams.uv_pitch = videoScratch.UVPitch;
390
391    /* Set up scaling */
392    df_set_video_filter_coefficients(NULL, 1);
393
394    err = df_set_video_scale(width, height, drawW, drawH,
395	DF_SCALEFLAG_CHANGEX | DF_SCALEFLAG_CHANGEY);
396    if (err != CIM_STATUS_OK) {
397	/* Note the problem, but do nothing for now. */
398	ErrorF("Video scale factor too large: %dx%d -> %dx%d\n",
399	    width, height, drawW, drawH);
400    }
401
402    /* Figure out clipping */
403
404    xend = dstBox->x2;
405    yend = dstBox->y2;
406
407    if (dstBox->y1 < 0) {
408	if (srcH < drawH)
409	    lines = ((-dstBox->y1) * srcH) / drawH;
410	else
411	    lines = (-dstBox->y1);
412
413	ystart = 0;
414	drawH += dstBox->y1;
415    } else {
416	ystart = dstBox->y1;
417	lines = 0;
418    }
419
420    yExtra = lines * videoScratch.dstPitch;
421    uvExtra = (lines >> 1) * videoScratch.UVPitch;
422
423    memset(&vidPos, 0, sizeof(vidPos));
424
425    vidPos.x = dstBox->x1;
426    vidPos.y = ystart;
427    vidPos.width = xend - dstBox->x1;
428    vidPos.height = yend - ystart;
429
430    df_set_video_position(&vidPos);
431
432    vSrcParams.y_offset = videoScratch.dstOffset + yExtra;
433
434    switch (id) {
435    case FOURCC_Y800:
436    case FOURCC_I420:
437	vSrcParams.u_offset = videoScratch.UDstOffset + uvExtra;
438	vSrcParams.v_offset = videoScratch.VDstOffset + uvExtra;
439	break;
440    case FOURCC_YV12:
441	vSrcParams.v_offset = videoScratch.UDstOffset + uvExtra;
442	vSrcParams.u_offset = videoScratch.VDstOffset + uvExtra;
443	break;
444
445    default:
446	vSrcParams.u_offset = vSrcParams.v_offset = 0;
447	break;
448    }
449
450    vSrcParams.flags = DF_SOURCEFLAG_IMPLICITSCALING;
451    df_configure_video_source(&vSrcParams, &vSrcParams);
452
453    /* Turn on the video palette */
454    df_set_video_palette(NULL);
455    df_set_video_enable(1, 0);
456}
457
458static int
459LXPutImage(ScrnInfoPtr pScrni,
460    short srcX, short srcY, short drawX, short drawY,
461    short srcW, short srcH, short drawW, short drawH,
462    int id, unsigned char *buf,
463    short width, short height, Bool sync, RegionPtr clipBoxes,
464    pointer data, DrawablePtr pDraw)
465{
466    GeodeRec *pGeode = GEODEPTR(pScrni);
467    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
468    INT32 x1, x2, y1, y2;
469    BoxRec dstBox;
470    Bool ret;
471
472    if (pGeode->rotation != RR_Rotate_0)
473	return Success;
474
475    if (srcW <= 0 || srcH <= 0) {
476	return Success;
477    }
478
479    if (drawW <= 0 || drawH <= 0) {
480	return Success;
481    }
482
483    if (drawW > 16384)
484	drawW = 16384;
485
486    memset(&videoScratch, 0, sizeof(videoScratch));
487
488    x1 = srcX;
489    x2 = srcX + srcW;
490    y1 = srcY;
491    y2 = srcY + srcH;
492
493    dstBox.x1 = drawX;
494    dstBox.x2 = drawX + drawW;
495    dstBox.y1 = drawY;
496    dstBox.y2 = drawY + drawH;
497
498    dstBox.x1 -= pScrni->frameX0;
499    dstBox.x2 -= pScrni->frameX0;
500    dstBox.y1 -= pScrni->frameY0;
501    dstBox.y2 -= pScrni->frameY0;
502
503    if (id == FOURCC_YV12 || id == FOURCC_I420)
504	ret = LXCopyPlanar(pScrni, id, buf, x1, y1, x2, y2, width,
505			height, data);
506    else
507	ret = LXCopyPacked(pScrni, id, buf, x1, y1, x2, y2, width,
508			height, data);
509
510    if (ret == FALSE)
511	return BadAlloc;
512
513    if (!RegionsEqual(&pPriv->clip, clipBoxes) ||
514	(drawW != pPriv->pwidth || drawH != pPriv->pheight)) {
515	REGION_COPY(pScrni->pScreen, &pPriv->clip, clipBoxes);
516
517	if (pPriv->colorKeyMode == 0) {
518	    xf86XVFillKeyHelper(pScrni->pScreen, pPriv->colorKey, clipBoxes);
519	}
520
521	LXDisplayVideo(pScrni, id, width, height, &dstBox,
522	    srcW, srcH, drawW, drawH);
523	pPriv->pwidth = drawW;
524	pPriv->pheight = drawH;
525    }
526
527    pPriv->videoStatus = CLIENT_VIDEO_ON;
528
529    return Success;
530}
531
532static void
533LXQueryBestSize(ScrnInfoPtr pScrni, Bool motion,
534    short vidW, short vidH, short drawW, short drawH,
535    unsigned int *retW, unsigned int *retH, pointer data)
536{
537    *retW = drawW > 16384 ? 16384 : drawW;
538    *retH = drawH;
539}
540
541static Atom xvColorKey, xvColorKeyMode, xvFilter;
542
543static int
544LXGetPortAttribute(ScrnInfoPtr pScrni,
545    Atom attribute, INT32 * value, pointer data)
546{
547    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
548
549    if (attribute == xvColorKey)
550	*value = pPriv->colorKey;
551    else if (attribute == xvColorKeyMode)
552	*value = pPriv->colorKeyMode;
553    else if (attribute == xvFilter)
554	*value = pPriv->filter;
555    else
556	return BadMatch;
557
558    return Success;
559}
560
561static int
562LXSetPortAttribute(ScrnInfoPtr pScrni,
563    Atom attribute, INT32 value, pointer data)
564{
565    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
566
567    gp_wait_until_idle();
568
569    if (attribute == xvColorKey) {
570	pPriv->colorKey = value;
571	LXSetColorkey(pScrni, pPriv);
572    } else if (attribute == xvColorKeyMode) {
573	pPriv->colorKeyMode = value;
574	LXSetColorkey(pScrni, pPriv);
575    } else if (attribute == xvFilter) {
576	if ((value < 0) || (value > 1))
577	    return BadValue;
578	pPriv->filter = value;
579    } else
580	return BadMatch;
581
582    return Success;
583}
584
585static void
586LXStopVideo(ScrnInfoPtr pScrni, pointer data, Bool exit)
587{
588    GeodePortPrivRec *pPriv = (GeodePortPrivRec *) data;
589
590    if (pPriv->videoStatus == 0)
591	return;
592
593    REGION_EMPTY(pScrni->pScreen, &pPriv->clip);
594    gp_wait_until_idle();
595
596    if (exit) {
597	if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
598	    unsigned int val;
599
600	    df_set_video_enable(0, 0);
601	    /* Put the LUT back in bypass */
602	    val = READ_VID32(DF_VID_MISC);
603	    WRITE_VID32(DF_VID_MISC, val | DF_GAMMA_BYPASS_BOTH);
604	}
605
606	if (pPriv->vidmem) {
607	    exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
608	    pPriv->vidmem = NULL;
609	}
610
611	pPriv->videoStatus = 0;
612
613	/* Eh? */
614    } else if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
615	pPriv->videoStatus |= OFF_TIMER;
616	pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
617    }
618}
619
620static void
621LXResetVideo(ScrnInfoPtr pScrni)
622{
623    GeodeRec *pGeode = GEODEPTR(pScrni);
624
625    if (!pGeode->NoAccel) {
626	GeodePortPrivRec *pPriv = pGeode->adaptor->pPortPrivates[0].ptr;
627
628	gp_wait_until_idle();
629	df_set_video_palette(NULL);
630
631	LXSetColorkey(pScrni, pPriv);
632    }
633}
634
635static void
636LXVidBlockHandler(int i, pointer blockData, pointer pTimeout,
637    pointer pReadmask)
638{
639    ScreenPtr pScrn = screenInfo.screens[i];
640    ScrnInfoPtr pScrni = xf86Screens[i];
641    GeodeRec *pGeode = GEODEPTR(pScrni);
642    GeodePortPrivRec *pPriv = GET_PORT_PRIVATE(pScrni);
643
644    pScrn->BlockHandler = pGeode->BlockHandler;
645    (*pScrn->BlockHandler) (i, blockData, pTimeout, pReadmask);
646    pScrn->BlockHandler = LXVidBlockHandler;
647
648    if (pPriv->videoStatus & TIMER_MASK) {
649	Time now = currentTime.milliseconds;
650
651	if (pPriv->videoStatus & OFF_TIMER) {
652	    gp_wait_until_idle();
653
654	    if (pPriv->offTime < now) {
655		unsigned int val;
656
657		df_set_video_enable(0, 0);
658		pPriv->videoStatus = FREE_TIMER;
659		pPriv->freeTime = now + FREE_DELAY;
660
661		/* Turn off the video palette */
662		val = READ_VID32(DF_VID_MISC);
663		WRITE_VID32(DF_VID_MISC, val | DF_GAMMA_BYPASS_BOTH);
664	    }
665	} else {
666	    if (pPriv->freeTime < now) {
667
668		if (pPriv->vidmem) {
669		    exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
670		    pPriv->vidmem = NULL;
671		}
672
673		pPriv->videoStatus = 0;
674	    }
675	}
676    }
677}
678
679static XF86VideoAdaptorPtr
680LXSetupImageVideo(ScreenPtr pScrn)
681{
682    ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
683    GeodeRec *pGeode = GEODEPTR(pScrni);
684    XF86VideoAdaptorPtr adapt;
685    GeodePortPrivRec *pPriv;
686
687    adapt = calloc(1, sizeof(XF86VideoAdaptorRec) +
688	sizeof(GeodePortPrivRec) + sizeof(DevUnion));
689
690    if (adapt == NULL) {
691	ErrorF("Couldn't create the rec\n");
692	return NULL;
693    }
694
695    adapt->type = XvWindowMask | XvInputMask | XvImageMask;
696    adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
697
698    adapt->name = "AMD Geode LX";
699    adapt->nEncodings = 1;
700    adapt->pEncodings = DummyEncoding;
701    adapt->nFormats = ARRAY_SIZE(Formats);
702    adapt->pFormats = Formats;
703    adapt->nPorts = 1;
704    adapt->pPortPrivates = (DevUnion *) (&adapt[1]);
705    pPriv = (GeodePortPrivRec *) (&adapt->pPortPrivates[1]);
706    adapt->pPortPrivates[0].ptr = (pointer) (pPriv);
707    adapt->pAttributes = Attributes;
708    adapt->nImages = ARRAY_SIZE(Images);
709    adapt->nAttributes = ARRAY_SIZE(Attributes);
710    adapt->pImages = Images;
711    adapt->PutVideo = NULL;
712    adapt->PutStill = NULL;
713    adapt->GetVideo = NULL;
714    adapt->GetStill = NULL;
715    adapt->StopVideo = LXStopVideo;
716    adapt->SetPortAttribute = LXSetPortAttribute;
717    adapt->GetPortAttribute = LXGetPortAttribute;
718    adapt->QueryBestSize = LXQueryBestSize;
719    adapt->PutImage = LXPutImage;
720
721    /* Use the common function */
722    adapt->QueryImageAttributes = GeodeQueryImageAttributes;
723
724    pPriv->vidmem = NULL;
725    pPriv->filter = 0;
726    pPriv->colorKey = 0;
727    pPriv->colorKeyMode = 0;
728    pPriv->videoStatus = 0;
729    pPriv->pwidth = 0;
730    pPriv->pheight = 0;
731
732    REGION_NULL(pScrn, &pPriv->clip);
733
734    pGeode->adaptor = adapt;
735
736    pGeode->BlockHandler = pScrn->BlockHandler;
737    pScrn->BlockHandler = LXVidBlockHandler;
738
739    xvColorKey = MAKE_ATOM("XV_COLORKEY");
740    xvColorKeyMode = MAKE_ATOM("XV_COLORKEYMODE");
741    xvFilter = MAKE_ATOM("XV_FILTER");
742
743    LXResetVideo(pScrni);
744
745    return adapt;
746}
747
748/* Offscreen surface allocation */
749
750struct OffscreenPrivRec
751{
752    ExaOffscreenArea *vidmem;
753    Bool isOn;
754};
755
756static int
757LXDisplaySurface(XF86SurfacePtr surface,
758    short srcX, short srcY, short drawX, short drawY,
759    short srcW, short srcH, short drawW, short drawH, RegionPtr clipBoxes)
760{
761    struct OffscreenPrivRec *pPriv =
762	(struct OffscreenPrivRec *)surface->devPrivate.ptr;
763
764    ScrnInfoPtr pScrni = surface->pScrn;
765    GeodePortPrivRec *portPriv = GET_PORT_PRIVATE(pScrni);
766
767    BoxRec dstBox;
768
769    dstBox.x1 = drawX;
770    dstBox.x2 = drawX + drawW;
771    dstBox.y1 = drawY;
772    dstBox.y2 = drawY + drawH;
773
774    if ((drawW <= 0) | (drawH <= 0))
775	return Success;
776
777    /* Is this still valid? */
778
779    dstBox.x1 -= pScrni->frameX0;
780    dstBox.x2 -= pScrni->frameX0;
781    dstBox.y1 -= pScrni->frameY0;
782    dstBox.y2 -= pScrni->frameY0;
783
784    xf86XVFillKeyHelper(pScrni->pScreen, portPriv->colorKey, clipBoxes);
785
786    videoScratch.dstOffset = surface->offsets[0];
787    videoScratch.dstPitch = surface->pitches[0];
788
789    LXDisplayVideo(pScrni, surface->id, surface->width, surface->height,
790	&dstBox, srcW, srcH, drawW, drawH);
791
792    pPriv->isOn = TRUE;
793
794    if (portPriv->videoStatus & CLIENT_VIDEO_ON) {
795	REGION_EMPTY(pScrni->pScreen, &portPriv->clip);
796	UpdateCurrentTime();
797	portPriv->videoStatus = FREE_TIMER;
798	portPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
799    }
800
801    return Success;
802}
803
804static int
805LXAllocateSurface(ScrnInfoPtr pScrni, int id, unsigned short w,
806    unsigned short h, XF86SurfacePtr surface)
807{
808    GeodeRec *pGeode = GEODEPTR(pScrni);
809    int pitch, lines;
810    ExaOffscreenArea *vidmem;
811    struct OffscreenPrivRec *pPriv;
812
813    if (w > 1024 || h > 1024)
814	return BadAlloc;
815
816    /* The width needs to be word aligned */
817    w = (w + 1) & ~1;
818
819    pitch = ((w << 1) + 15) & ~15;
820    lines = ((pitch * h) + (pGeode->Pitch - 1)) / pGeode->Pitch;
821
822    /* FIXME: is lines the right parameter to use here,
823     * or should it be height * pitch? */
824    vidmem = exaOffscreenAlloc(pScrni->pScreen, lines, 4, TRUE,
825		NULL, NULL);
826
827    if (vidmem == NULL) {
828	ErrorF("Error while allocating an offscreen region.\n");
829	return BadAlloc;
830    }
831
832    surface->width = w;
833    surface->height = h;
834
835    surface->pitches = malloc(sizeof(int));
836
837    surface->offsets = malloc(sizeof(int));
838
839    pPriv = malloc(sizeof(struct OffscreenPrivRec));
840
841    if (pPriv && surface->pitches && surface->offsets) {
842
843	pPriv->vidmem = vidmem;
844
845	pPriv->isOn = FALSE;
846
847	surface->pScrn = pScrni;
848	surface->id = id;
849	surface->pitches[0] = pitch;
850	surface->offsets[0] = vidmem->offset;
851	surface->devPrivate.ptr = (pointer) pPriv;
852
853	return Success;
854    }
855
856    if (surface->offsets)
857	free(surface->offsets);
858
859    if (surface->pitches)
860	free(surface->pitches);
861
862    if (vidmem) {
863	exaOffscreenFree(pScrni->pScreen, vidmem);
864	vidmem = NULL;
865    }
866
867    return BadAlloc;
868}
869
870static int
871LXStopSurface(XF86SurfacePtr surface)
872{
873    struct OffscreenPrivRec *pPriv = (struct OffscreenPrivRec *)
874	surface->devPrivate.ptr;
875
876    pPriv->isOn = FALSE;
877    return Success;
878}
879
880static int
881LXFreeSurface(XF86SurfacePtr surface)
882{
883    struct OffscreenPrivRec *pPriv = (struct OffscreenPrivRec *)
884	surface->devPrivate.ptr;
885    ScrnInfoPtr pScrni = surface->pScrn;
886
887    if (pPriv->isOn)
888	LXStopSurface(surface);
889
890    if (pPriv->vidmem) {
891	exaOffscreenFree(pScrni->pScreen, pPriv->vidmem);
892	pPriv->vidmem = NULL;
893    }
894
895    free(surface->pitches);
896    free(surface->offsets);
897    free(surface->devPrivate.ptr);
898
899    return Success;
900}
901
902static int
903LXGetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 * value)
904{
905    return LXGetPortAttribute(pScrni, attribute, value,
906	(pointer) (GET_PORT_PRIVATE(pScrni)));
907}
908
909static int
910LXSetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 value)
911{
912    return LXSetPortAttribute(pScrni, attribute, value,
913	(pointer) (GET_PORT_PRIVATE(pScrni)));
914}
915
916static void
917LXInitOffscreenImages(ScreenPtr pScrn)
918{
919    XF86OffscreenImagePtr offscreenImages;
920
921    /* need to free this someplace */
922    if (!(offscreenImages = malloc(sizeof(XF86OffscreenImageRec))))
923	return;
924
925    offscreenImages[0].image = &Images[0];
926    offscreenImages[0].flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
927    offscreenImages[0].alloc_surface = LXAllocateSurface;
928    offscreenImages[0].free_surface = LXFreeSurface;
929    offscreenImages[0].display = LXDisplaySurface;
930    offscreenImages[0].stop = LXStopSurface;
931    offscreenImages[0].setAttribute = LXSetSurfaceAttribute;
932    offscreenImages[0].getAttribute = LXGetSurfaceAttribute;
933    offscreenImages[0].max_width = 1024;
934    offscreenImages[0].max_height = 1024;
935    offscreenImages[0].num_attributes = ARRAY_SIZE(Attributes);
936    offscreenImages[0].attributes = Attributes;
937
938    xf86XVRegisterOffscreenImages(pScrn, offscreenImages, 1);
939}
940
941void
942LXInitVideo(ScreenPtr pScrn)
943{
944    GeodeRec *pGeode;
945    ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
946    XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
947    XF86VideoAdaptorPtr newAdaptor = NULL;
948    int num_adaptors;
949
950    pGeode = GEODEPTR(pScrni);
951
952    if (pGeode->NoAccel) {
953	ErrorF("Cannot run Xv without accelerations!\n");
954	return;
955    }
956
957    if (!(newAdaptor = LXSetupImageVideo(pScrn))) {
958	ErrorF("Error while setting up the adaptor.\n");
959	return;
960    }
961
962    LXInitOffscreenImages(pScrn);
963
964    num_adaptors = xf86XVListGenericAdaptors(pScrni, &adaptors);
965
966    if (!num_adaptors) {
967	num_adaptors = 1;
968	adaptors = &newAdaptor;
969    } else {
970	newAdaptors =
971	    malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr *));
972
973	if (newAdaptors) {
974	    memcpy(newAdaptors, adaptors, num_adaptors *
975		sizeof(XF86VideoAdaptorPtr));
976	    newAdaptors[num_adaptors] = newAdaptor;
977	    adaptors = newAdaptors;
978	    num_adaptors++;
979	} else
980	    ErrorF("Memory error while setting up the adaptor\n");
981    }
982
983    if (num_adaptors)
984	xf86XVScreenInit(pScrn, adaptors, num_adaptors);
985
986    if (newAdaptors)
987	free(newAdaptors);
988}
989