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