/* * Copyright 2001 Ani Joshi * * XFree86 4.x driver for S3 chipsets * * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation and * that the name of Ani Joshi not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Ani Joshi makes no representations * about the suitability of this software for any purpose. It is provided * "as-is" without express or implied warranty. * * ANI JOSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL ANI JOSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "xf86.h" #include "xf86_OSproc.h" #include "compiler.h" #include "s3.h" #include "s3_reg.h" #define CLIENT_VIDEO_ON 0x04 #define S3_MAX_PORTS 1 #define NUM_FORMATS_OVERLAY 4 #define NUM_FORMATS_TEXTURE 4 static XF86VideoAdaptorPtr S3AllocAdaptor(ScrnInfoPtr pScrn); static XF86VideoAdaptorPtr S3SetupImageVideoOverlay(ScreenPtr); static int S3SetPortAttributeOverlay(ScrnInfoPtr, Atom, INT32, pointer); static int S3GetPortAttributeOverlay(ScrnInfoPtr, Atom ,INT32 *, pointer); static void S3StopVideo(ScrnInfoPtr, pointer, Bool); static void S3QueryBestSize(ScrnInfoPtr, Bool, short, short, short, short, unsigned int *, unsigned int *, pointer); static int S3PutImage(ScrnInfoPtr, short, short, short, short, short, short, short, short, int, unsigned char*, short, short, Bool, RegionPtr, pointer, DrawablePtr); static int S3QueryImageAttributes(ScrnInfoPtr, int, unsigned short *, unsigned short *, int *, int *); static void S3ResetVideoOverlay(ScrnInfoPtr); static FBLinearPtr S3XVMemAlloc(ScrnInfoPtr pScrn, pointer pVideo, int size); void S3InitVideo(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL; XF86VideoAdaptorPtr newAdaptor = NULL; int num_adaptors; newAdaptor = S3SetupImageVideoOverlay(pScreen); xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using overlay video\n"); num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors); if(newAdaptor) { if(!num_adaptors) { num_adaptors = 1; adaptors = &newAdaptor; } else { newAdaptors = /* need to free this someplace */ malloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr*)); if(newAdaptors) { memcpy(newAdaptors, adaptors, num_adaptors * sizeof(XF86VideoAdaptorPtr)); newAdaptors[num_adaptors] = newAdaptor; adaptors = newAdaptors; num_adaptors++; } } } if(num_adaptors) xf86XVScreenInit(pScreen, adaptors, num_adaptors); free(newAdaptors); } /* client libraries expect an encoding */ static XF86VideoEncodingRec DummyEncoding[2] = { { /* overlay limit */ 0, "XV_IMAGE", 1024, 1024, {1, 1} }, { /* texture limit */ 0, "XV_IMAGE", 2046, 2046, {1, 1} } }; static FBLinearPtr S3XVMemAlloc(ScrnInfoPtr pScrn, pointer pVideo, int size) { FBLinearPtr pLinear = (FBLinearPtr)pVideo; ScreenPtr pScreen = pScrn->pScreen; if (pLinear) { if ((pLinear->size >= size) || xf86ResizeOffscreenLinear(pLinear, size)) { pLinear->MoveLinearCallback = NULL; pLinear->RemoveLinearCallback = NULL; return pLinear; } xf86FreeOffscreenLinear(pLinear); } pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16, NULL, NULL, NULL); if (!pLinear) { int maxSize; xf86QueryLargestOffscreenLinear(pScreen, &maxSize, 16, PRIORITY_EXTREME); if (maxSize < size) return NULL; xf86PurgeUnlockedOffscreenAreas(pScreen); pLinear = xf86AllocateOffscreenLinear(pScreen, size, 16, NULL, NULL, NULL); } return pLinear; } static XF86VideoFormatRec Formats[NUM_FORMATS_TEXTURE] = { {16, TrueColor}, {24, TrueColor}, {16, DirectColor}, {24, DirectColor} }; #define NUM_IMAGES 3 static XF86ImageRec Images[NUM_IMAGES] = { XVIMAGE_YUY2, /* As in mga, YV12 & I420 are converted to YUY2 on the fly by */ /* copy over conversion. */ XVIMAGE_YV12, XVIMAGE_I420 /* XVIMAGE_UYVY */ }; static int S3SetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute, INT32 value, pointer data) { return BadMatch; } static int S3GetPortAttributeOverlay(ScrnInfoPtr pScrn, Atom attribute, INT32 *value, pointer data) { return BadMatch; } static void S3QueryBestSize(ScrnInfoPtr pScrn, Bool motion, short vid_w, short vid_h, short drw_w, short drw_h, unsigned int *p_w, unsigned int *p_h, pointer data) { *p_w = drw_w; *p_h = drw_h; } static void S3ResetVideoOverlay(ScrnInfoPtr pScrn) { } static XF86VideoAdaptorPtr S3AllocAdaptor(ScrnInfoPtr pScrn) { S3Ptr pS3 = S3PTR(pScrn); XF86VideoAdaptorPtr adapt; S3PortPrivPtr pPriv; int i; if(!(adapt = xf86XVAllocateVideoAdaptorRec(pScrn))) return NULL; if(!(pPriv = calloc(1, sizeof(S3PortPrivRec) + (sizeof(DevUnion) * S3_MAX_PORTS)))) { free(adapt); return NULL; } adapt->pPortPrivates = (DevUnion*)(&pPriv[1]); for(i = 0; i < S3_MAX_PORTS; i++) adapt->pPortPrivates[i].val = i; pPriv->colorKey = (1 << pScrn->offset.red) | (1 << pScrn->offset.green) | (((pScrn->mask.blue >> pScrn->offset.blue) - 1) << pScrn->offset.blue); pPriv->videoStatus = 0; pPriv->lastPort = -1; pS3->adaptor = adapt; pS3->portPrivate = pPriv; return adapt; } static XF86VideoAdaptorPtr S3SetupImageVideoOverlay(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); S3Ptr pS3 = S3PTR(pScrn); XF86VideoAdaptorPtr adapt; adapt = S3AllocAdaptor(pScrn); if (adapt == NULL) return NULL; adapt->type = XvWindowMask | XvInputMask | XvImageMask; adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT; adapt->name = "S3 Backend Scaler"; adapt->nEncodings = 1; adapt->pEncodings = &DummyEncoding[0]; adapt->nFormats = NUM_FORMATS_OVERLAY; adapt->pFormats = Formats; adapt->nPorts = 1; adapt->pAttributes = NULL /*Attributes*/; adapt->nImages = 3; adapt->nAttributes = 0; adapt->pImages = Images; adapt->PutVideo = NULL; adapt->PutStill = NULL; adapt->GetVideo = NULL; adapt->GetStill = NULL; adapt->StopVideo = S3StopVideo; /* Empty Attrib functions - required anyway */ adapt->SetPortAttribute = S3SetPortAttributeOverlay; adapt->GetPortAttribute = S3GetPortAttributeOverlay; adapt->QueryBestSize = S3QueryBestSize; adapt->PutImage = S3PutImage; adapt->QueryImageAttributes = S3QueryImageAttributes; /* gotta uninit this someplace */ REGION_NULL(pScreen, &(pS3->portPrivate->clip)); S3ResetVideoOverlay(pScrn); return adapt; } static void S3StopVideo(ScrnInfoPtr pScrn, pointer data, Bool exit) { S3Ptr pS3 = S3PTR(pScrn); S3PortPrivPtr pPriv = pS3->portPrivate; REGION_EMPTY(pScrn->pScreen, &pPriv->clip); if (exit) { SET_FIFO_CNTL(0x00080000 | FIFO_PS24_SS0); if (pPriv->videoStatus & CLIENT_VIDEO_ON) { WaitVSync(); SET_SSTREAM_CNTL(0x03000000); SET_SSTREAM_FBADDR0(0x00000000); SET_SSTREAM_FBADDR1(0x00000000); SET_SSTREAM_STRIDE(0x00000001); SET_SSTREAM_START(0x07ff07ff); SET_SSTREAM_WIND(0x00010001); SET_CHROMA_KEY(0x00000000); SET_SSTRETCH(0x00000000); SET_OPAQUE_OVERLAY(0x40000000); SET_K1_VSCALE(0x00000000); SET_K2_VSCALE(0x00000000); SET_DDA_VERT(0x00000000); SET_BLEND_CNTL(0x01000000); WaitVSync(); } if (pPriv->area) { xf86FreeOffscreenLinear(pPriv->area); pPriv->area = NULL; } pPriv->videoStatus = 0; } } static void S3DisplayVideoOverlay(ScrnInfoPtr pScrn, int id, int offset, short width, short height, int pitch, int x1, int y1, int x2, int y2, BoxPtr dstBox, short src_w, short src_h, short drw_w, short drw_h) { S3Ptr pS3 = S3PTR(pScrn); S3PortPrivPtr pPriv = pS3->portPrivate; int tmp; if (drw_w == src_w) tmp = 0; else tmp = 2; SET_SSTREAM_CNTL((tmp << 28) | 0x01000000 | ((((src_w - 1) << 1) - (drw_w - 1)) & 0xfff)); SET_SSTRETCH(((src_w - 1) & 0x7ff) | (((src_w - drw_w) & 0x7ff) << 16)); SET_BLEND_CNTL(0x05000000); SET_SSTREAM_FBADDR0(offset & 0x3fffff); SET_SSTREAM_FBADDR1(offset & 0x3fffff); SET_SSTREAM_STRIDE(pitch & 0xfff); SET_SSTREAM_START(((dstBox->x1 + 1) << 16) | (dstBox->y1 + 1)); SET_SSTREAM_WIND((((drw_w - 1) << 16) | drw_h) & 0x7ff07ff); SET_K1_VSCALE(src_h - 1); SET_K2_VSCALE((src_h - drw_h) & 0x7ff); SET_DDA_VERT(((~drw_h - 1)) & 0xfff); SET_CHROMA_KEY(0x10000000 | ((pScrn->weight.red-1) << 24) | ((pPriv->colorKey & pScrn->mask.red) >> pScrn->offset.red) << (16 + 8-pScrn->weight.red) | ((pPriv->colorKey & pScrn->mask.green) >> pScrn->offset.green) << (8 + 8-pScrn->weight.green) | ((pPriv->colorKey & pScrn->mask.blue) >> pScrn->offset.blue) << (8-pScrn->weight.blue)); SET_FIFO_CNTL(0x00080000 | pS3->Streams_FIFO); } static int S3PutImage(ScrnInfoPtr pScrn, short src_x, short src_y, short drw_x, short drw_y, short src_w, short src_h, short drw_w, short drw_h, int id, unsigned char *buf, short width, short height, Bool sync, RegionPtr clipBoxes, pointer data, DrawablePtr pDraw) { S3Ptr pS3 = S3PTR(pScrn); S3PortPrivPtr pPriv = pS3->portPrivate; INT32 x1, x2, y1, y2; CARD8 *dst_start; int offset, offsetV = 0, offsetU = 0; int srcPitch, srcPitchUV = 0, dstPitch, dstSize; int top, bottom, right, left, npixels, nlines; BoxRec dstBox; CARD32 tmp; int cpp = (pScrn->bitsPerPixel + 7) >> 3; /* Clip */ x1 = src_x; x2 = src_x + src_w; y1 = src_y; y2 = src_y + src_h; dstBox.x1 = drw_x; dstBox.x2 = drw_x + drw_w; dstBox.y1 = drw_y; dstBox.y2 = drw_y + drw_h; if(!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes, width, height)) return Success; dstBox.x1 -= pScrn->frameX0; dstBox.x2 -= pScrn->frameX0; dstBox.y1 -= pScrn->frameY0; dstBox.y2 -= pScrn->frameY0; /* requested size in bytes */ dstPitch = ((width << 1) + 15) & ~15; dstSize = dstPitch * height; pPriv->area = S3XVMemAlloc(pScrn, pPriv->area, (dstSize + cpp - 1) / cpp); if (!pPriv->area) return BadAlloc; offset = pPriv->area->offset * cpp; dst_start = pS3->FBBase + offset; switch (id) { case FOURCC_YV12: case FOURCC_I420: left = (x1 >> 16) & ~1; right = ((x2 + 0x1ffff) >> 16) & ~1; top = (y1 >> 16) & ~1; bottom = ((y2 + 0x1ffff) >> 16) & ~1; if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff))) right += 2; if ((bottom < height) && ((y1 & 0x1ffff) <= (y2 & 0x1ffff))) bottom += 2; npixels = right - left; nlines = bottom - top; srcPitch = (width + 3) & ~3; offsetV = srcPitch * height; srcPitchUV = ((width >> 1) + 3) & ~3; offsetU = ((height >> 1) * srcPitchUV) + offsetV; tmp = ((top >> 1) * srcPitchUV) + (left >> 1); offsetV += tmp; offsetU += tmp; if (id == FOURCC_I420) { tmp = offsetV; offsetV = offsetU; offsetU = tmp; } dst_start += top * dstPitch + (left << 1); xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + left, buf + offsetV, buf + offsetU, dst_start, srcPitch, srcPitchUV, dstPitch, nlines, npixels); break; case FOURCC_UYVY: case FOURCC_YUY2: default: left = (x1 >> 16) & ~1; right = ((x2 + 0x1ffff) >> 16) & ~1; top = y1 >> 16; bottom = (y2 + 0x0ffff) >> 16; if ((right < width) && ((x1 & 0x1ffff) <= (x2 & 0x1ffff))) right += 2; if ((bottom < height) && ((y1 & 0x0ffff) <= (y2 & 0x0ffff))) bottom++; npixels = right - left; nlines = bottom - top; srcPitch = width << 1; buf += (top * srcPitch) + (left << 1); dst_start += top * dstPitch + (left << 1); xf86XVCopyPacked(buf, dst_start, srcPitch, dstPitch, nlines, npixels); break; } /* update cliplist */ if(!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) { REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes); /* draw these */ xf86XVFillKeyHelper(pScrn->pScreen, pPriv->colorKey, clipBoxes); } offset += (left << 1) + (top * dstPitch); S3DisplayVideoOverlay(pScrn, id, offset, width, height, dstPitch, x1, y1, x2, y2, &dstBox, src_w, src_h, drw_w, drw_h); pPriv->videoStatus = CLIENT_VIDEO_ON; return Success; } static int S3QueryImageAttributes(ScrnInfoPtr pScrn, int id, unsigned short *w, unsigned short *h, int *pitches, int *offsets) { int size, tmp; *w = (*w + 1) & ~1; if(offsets) offsets[0] = 0; switch(id) { case FOURCC_YV12: case FOURCC_I420: *h = (*h + 1) & ~1; size = (*w + 3) & ~3; if(pitches) pitches[0] = size; size *= *h; if(offsets) offsets[1] = size; tmp = ((*w >> 1) + 3) & ~3; if(pitches) pitches[1] = pitches[2] = tmp; tmp *= (*h >> 1); size += tmp; if(offsets) offsets[2] = size; size += tmp; break; case FOURCC_UYVY: case FOURCC_YUY2: default: size = *w << 1; if(pitches) pitches[0] = size; size *= *h; break; } return size; } void S3InitStreams(ScrnInfoPtr pScrn, DisplayModePtr mode) { S3Ptr pS3 = S3PTR(pScrn); unsigned int pst_wind = (mode->HDisplay-1) << 16 | (mode->VDisplay); WaitVSync(); switch (pScrn->bitsPerPixel) { case 8: SET_PSTREAM_CNTL(0x00000000); break; case 15: SET_PSTREAM_CNTL(0x03000000); break; case 16: SET_PSTREAM_CNTL(0x05000000); break; case 24: SET_PSTREAM_CNTL(0x06000000); break; case 32: SET_PSTREAM_CNTL(0x07000000); break; } SET_PSTREAM_FBADDR0(0x00000000); SET_PSTREAM_FBADDR1(0x00000000); SET_PSTREAM_STRIDE(pS3->s3BppDisplayWidth & 0x0fff); SET_PSTREAM_WIND(pst_wind & 0x07ff07ff); SET_PSTREAM_START(0x00010001); SET_CHROMA_KEY(0x00000000); SET_SSTRETCH(0x00000000); SET_BLEND_CNTL(0x01000000); SET_DOUBLE_BUFFER(0x00000000); SET_SSTREAM_CNTL(0x03000000); SET_SSTREAM_FBADDR0(0x00000000); SET_SSTREAM_FBADDR1(0x00000000); SET_SSTREAM_STRIDE(0x00000001); SET_SSTREAM_START(0x07ff07ff); SET_SSTREAM_WIND(0x00010001); SET_OPAQUE_OVERLAY(0x40000000); SET_K1_VSCALE(0x00000000); SET_K2_VSCALE(0x00000000); SET_DDA_VERT(0x00000000); /* ps thr | ss thr | ss fifo slots set primary stream FIFO to 24 slots and 12 slots for threshold */ SET_FIFO_CNTL(0x00080000 | FIFO_PS24_SS0); }