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